In today’s digital age, managing contacts efficiently is crucial, whether for personal or professional use. Imagine having a centralized, easily accessible address book that you can access from anywhere. This tutorial will guide you through building a simple, yet functional, web-based address book using Node.js, Express, and a touch of HTML, CSS, and JavaScript. This project is ideal for beginners and intermediate developers looking to enhance their skills in web development and backend programming. By the end of this tutorial, you’ll have a fully operational application that allows you to add, view, edit, and delete contacts.
Why Build an Address Book?
Creating a web-based address book offers several advantages over traditional methods like spreadsheets or paper notebooks. First, it provides easy accessibility from any device with an internet connection. Second, it allows for efficient searching and filtering of contacts. Third, it simplifies contact management, eliminating the need to manually update multiple sources. Finally, it serves as a practical project to learn and apply fundamental web development concepts, including server-side programming, database interaction (though we’ll use in-memory storage for simplicity), and front-end design.
Prerequisites
Before we begin, ensure you have the following installed on your system:
- Node.js and npm (Node Package Manager): Download and install Node.js. npm comes bundled with Node.js.
- A text editor or IDE (e.g., VS Code, Sublime Text, Atom)
- Basic knowledge of HTML, CSS, and JavaScript (familiarity with these languages will be helpful but not strictly required)
Setting Up the Project
Let’s start by creating a new project directory and initializing our Node.js project. Open your terminal or command prompt and run the following commands:
mkdir address-book
cd address-book
npm init -y
The `npm init -y` command will create a `package.json` file, which manages our project’s dependencies.
Installing Dependencies
We’ll need a few dependencies for our project:
- Express: A web application framework for Node.js.
- Body-parser: Middleware to parse request bodies (e.g., JSON) from incoming requests.
- EJS (Embedded JavaScript): A templating engine to generate HTML dynamically.
Install these dependencies using npm:
npm install express body-parser ejs
Creating the Server (server.js)
Now, let’s create the core of our application, the server. Create a file named `server.js` in your project directory and add the following code:
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;
// Middleware
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public')); // Serve static files (CSS, JS, images)
app.set('view engine', 'ejs'); // Set EJS as the view engine
// In-memory data store (for simplicity - in a real app, use a database)
let contacts = [
{ id: 1, name: 'Alice Smith', phone: '123-456-7890', email: 'alice@example.com' },
{ id: 2, name: 'Bob Johnson', phone: '987-654-3210', email: 'bob@example.com' }
];
// Routes
app.get('/', (req, res) => {
res.render('index', { contacts: contacts });
});
app.get('/add', (req, res) => {
res.render('add');
});
app.post('/add', (req, res) => {
const newContact = {
id: contacts.length + 1,
name: req.body.name,
phone: req.body.phone,
email: req.body.email
};
contacts.push(newContact);
res.redirect('/');
});
app.get('/edit/:id', (req, res) => {
const contactId = parseInt(req.params.id);
const contact = contacts.find(contact => contact.id === contactId);
res.render('edit', { contact: contact });
});
app.post('/edit/:id', (req, res) => {
const contactId = parseInt(req.params.id);
const contactIndex = contacts.findIndex(contact => contact.id === contactId);
if (contactIndex !== -1) {
contacts[contactIndex] = {
id: contactId,
name: req.body.name,
phone: req.body.phone,
email: req.body.email
};
}
res.redirect('/');
});
app.get('/delete/:id', (req, res) => {
const contactId = parseInt(req.params.id);
contacts = contacts.filter(contact => contact.id !== contactId);
res.redirect('/');
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
Let’s break down this code:
- We import the necessary modules: `express` for the web server, `body-parser` to parse form data, and `ejs` for templating.
- We set up middleware: `bodyParser.urlencoded({ extended: true })` to parse URL-encoded data, `express.static(‘public’)` to serve static files (like CSS and JavaScript), and `app.set(‘view engine’, ‘ejs’)` to tell Express to use EJS for rendering views.
- We create an in-memory `contacts` array to store our contact data. Important: In a real-world application, you would use a database like MongoDB, PostgreSQL, or MySQL to persist this data.
- We define routes (using `app.get` and `app.post`) to handle different HTTP requests. These routes include:
/: Displays the list of contacts (renders the `index.ejs` view)./add: Displays the form to add a new contact (renders the `add.ejs` view)./add(POST): Handles the submission of the add contact form. It creates a new contact object and adds it to the `contacts` array. Then, it redirects back to the homepage./edit/:id: Displays the form to edit a contact (renders the `edit.ejs` view). The `:id` is a route parameter representing the contact’s ID./edit/:id(POST): Handles the submission of the edit contact form. It updates the contact with the given ID. Then, it redirects back to the homepage./delete/:id: Deletes the contact with the given ID. Then, it redirects back to the homepage.- Finally, we start the server using `app.listen(port, () => { … })`.
Creating Views with EJS
Now, let’s create the EJS view files. Create a directory named `views` in your project directory. Inside the `views` directory, create the following files:
- `index.ejs`
- `add.ejs`
- `edit.ejs`
index.ejs: This file will display the list of contacts.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Address Book</title>
<link rel="stylesheet" href="/style.css"> <!-- Link to your CSS file -->
</head>
<body>
<h2>Contacts</h2>
<a href="/add">Add Contact</a>
<table>
<thead>
<tr>
<th>Name</th>
<th>Phone</th>
<th>Email</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% contacts.forEach(contact => { %>
<tr>
<td><%= contact.name %></td>
<td><%= contact.phone %></td>
<td><%= contact.email %></td>
<td>
<a href="/edit/<%= contact.id %>">Edit</a> |
<a href="/delete/<%= contact.id %>" onclick="return confirm('Are you sure?')">Delete</a>
</td>
</tr>
<% }); %>
</tbody>
</table>
</body>
</html>
add.ejs: This file will contain the form to add a new contact.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Add Contact</title>
<link rel="stylesheet" href="/style.css"> <!-- Link to your CSS file -->
</head>
<body>
<h2>Add Contact</h2>
<form action="/add" method="POST">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required><br><br>
<label for="phone">Phone:</label>
<input type="text" id="phone" name="phone"><br><br>
<label for="email">Email:</label>
<input type="email" id="email" name="email"><br><br>
<button type="submit">Add</button>
<a href="/">Cancel</a>
</form>
</body>
</html>
edit.ejs: This file will contain the form to edit an existing contact.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Contact</title>
<link rel="stylesheet" href="/style.css"> <!-- Link to your CSS file -->
</head>
<body>
<h2>Edit Contact</h2>
<form action="/edit/<%= contact.id %>" method="POST">
<label for="name">Name:</label>
<input type="text" id="name" name="name" value="<%= contact.name %>" required><br><br>
<label for="phone">Phone:</label>
<input type="text" id="phone" name="phone" value="<%= contact.phone %>"><br><br>
<label for="email">Email:</label>
<input type="email" id="email" name="email" value="<%= contact.email %>"><br><br>
<button type="submit">Save</button>
<a href="/">Cancel</a>
</form>
</body>
</html>
Let’s examine the EJS files:
- index.ejs: This file displays the list of contacts in a table. It uses a loop (`<% contacts.forEach(contact => { %> … <% }); %>`) to iterate through the `contacts` array (passed from `server.js`) and dynamically generate table rows for each contact. It includes links to edit and delete contacts.
- add.ejs: This file contains a form with input fields for name, phone, and email. The form’s `action` attribute is set to `/add` (POST), and the `method` is set to `POST`. The `required` attribute on the name field ensures that the user must enter a name.
- edit.ejs: Similar to `add.ejs`, this file contains a form to edit an existing contact. It pre-fills the input fields with the existing contact’s data using EJS tags (`<%= contact.name %>`, etc.). The form’s `action` attribute is set to `/edit/<%= contact.id %>` (POST), and the `method` is set to `POST`.
Adding CSS (style.css)
Create a `public` directory in your project directory. Inside the `public` directory, create a file named `style.css`. This file will hold your CSS styles. Add some basic styles to make the address book look presentable:
body {
font-family: sans-serif;
margin: 20px;
}
h2 {
margin-bottom: 10px;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
a {
text-decoration: none;
color: blue;
margin-right: 10px;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"], input[type="email"] {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box; /* Important for width calculation */
}
button {
background-color: #4CAF50;
color: white;
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #3e8e41;
}
This CSS provides basic styling for the body, headings, tables, links, and forms.
Running the Application
Now, let’s run the application. In your terminal, navigate to your project directory and run the following command:
node server.js
You should see the message “Server is running on http://localhost:3000” in your terminal. Open your web browser and go to http://localhost:3000. You should see the address book application. You can add, edit, and delete contacts.
Common Mistakes and Troubleshooting
Here are some common mistakes and how to fix them:
- Incorrect File Paths: Double-check that your file paths in the `server.js` file (e.g., for serving static files and using the view engine) are correct.
- Typographical Errors: Carefully check for typos in your code, especially in variable names, route definitions, and EJS tags.
- Missing Dependencies: Ensure you have installed all the necessary dependencies using `npm install`.
- Incorrect Form Handling: Make sure the `method` attribute in your HTML forms is set to `POST` when submitting data. Also, ensure you have `body-parser` middleware set up correctly to parse the form data.
- Server Not Running: If the server isn’t running, check the terminal for any error messages. Make sure you are in the correct directory when running `node server.js`.
- CSS Not Applied: Ensure the link to your `style.css` file in your HTML is correct (e.g., `<link rel=”stylesheet” href=”/style.css”>`). Also, make sure the `public` directory is set up correctly in `server.js`.
- EJS Syntax Errors: Double-check your EJS syntax (e.g., using the correct tags like `<%= … %>` and `<% … %>`).
Enhancements and Next Steps
This is a basic address book. Here are some ways to enhance it:
- Database Integration: Replace the in-memory `contacts` array with a database (e.g., MongoDB, PostgreSQL, or MySQL) to persist the data. This is the most crucial step for a real-world application.
- Input Validation: Add input validation on the server-side to ensure that the data entered by the user is valid (e.g., email format, phone number format).
- Search Functionality: Implement a search feature to quickly find contacts.
- Sorting and Filtering: Allow users to sort and filter contacts by name, phone, or email.
- User Authentication: Add user authentication to protect the address book and allow multiple users.
- Error Handling: Implement proper error handling to gracefully handle errors and provide informative messages to the user.
- Styling and UI/UX improvements: Improve the look and feel of the application with more advanced CSS and potentially JavaScript libraries.
- Deployment: Deploy the application to a platform like Heroku, AWS, or Google Cloud Platform to make it accessible online.
Summary / Key Takeaways
You’ve successfully built a functional web-based address book using Node.js, Express, and EJS. You’ve learned how to set up a Node.js project, install dependencies, create server routes, use a templating engine to generate dynamic HTML, and handle user input. This project provides a solid foundation for understanding web development with Node.js. Remember to focus on database integration for a production-ready application and consider the enhancements mentioned above to expand your skills. By continuing to practice and build projects, you’ll become more proficient in web development. This address book project allows you to practice essential skills such as routing, handling HTTP requests, working with forms, and rendering dynamic content. This is a great starting point for building more complex web applications.
FAQ
1. Can I use a different templating engine instead of EJS?
Yes, you can. Popular alternatives include Pug (formerly Jade) and Handlebars. The core concepts of routing and handling data remain the same; you’d just need to adjust the view rendering part.
2. How do I deploy this application?
You can deploy your application to platforms like Heroku, AWS, or Google Cloud Platform. You’ll need to create an account, set up a deployment pipeline, and configure your application to run on the platform.
3. Why is the in-memory data store not suitable for production?
The in-memory data store is volatile; data is lost when the server restarts. A database persists the data, making it suitable for production environments where data needs to be stored permanently.
4. How can I improve the security of my address book?
You can improve security by implementing user authentication, validating user input on the server-side, using HTTPS, and protecting against common web vulnerabilities like cross-site scripting (XSS) and cross-site request forgery (CSRF).
5. Where can I learn more about Node.js and Express?
The official Node.js and Express documentation are excellent resources. You can also find many online tutorials, courses, and communities (e.g., Stack Overflow, Reddit) to help you learn and troubleshoot issues.
Building this address book is a valuable learning experience. By modifying and expanding upon it, you can gain a deeper understanding of web development and hone your skills. Remember, the best way to learn is by doing, so continue to explore and experiment with different features and technologies.
