In this guide, we will create a contact management system. The backend will be powered by Node.js and Express.js, and the frontend will use React 18. The app will allow users to add, view, edit, and delete contacts.
Prerequisites
Before getting started, ensure you have:
- Node.js v20.24 installed (check with
node -v
) - React 18 familiarity
- Basic knowledge of REST APIs and Express.js
1. Setting up the Backend (Node.js + Express.js)
Step 1: Initialize a new Node.js project
First, create a directory for your project and initialize it with npm:
mkdir contact-manager
cd contact-manager
npm init -y
Step 2: Install required packages
We need Express.js for the server and mongoose for interacting with MongoDB. Install these dependencies:
npm install express mongoose cors
We also need nodemon for auto-reloading during development:
npm install --save-dev nodemon
Update the package.json
to include a start script:
"scripts": {
"start": "nodemon index.js"
}
Step 3: Set up the Express server
Create a file named index.js
and set up a simple server with Express:
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const app = express();
app.use(express.json()); // for parsing application/json
app.use(cors()); // to enable CORS
// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/contact-manager', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log('MongoDB connected'))
.catch((err) => console.log(err));
// Start the server
const PORT = 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
This sets up the basic Express server that listens on port 5000 and connects to a MongoDB database named contact-manager
.
Step 4: Define the Contact model
Now, we’ll define a mongoose model to represent our contact schema. Create a models
folder and inside it, create a file called Contact.js
:
const mongoose = require('mongoose');
const ContactSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true },
phone: { type: String, required: true },
});
module.exports = mongoose.model('Contact', ContactSchema);
Step 5: Create API routes for CRUD operations
Next, define the API routes for the CRUD operations. Update index.js
as follows:
const Contact = require('./models/Contact');
// Create a new contact
app.post('/contacts', async (req, res) => {
try {
const contact = new Contact(req.body);
await contact.save();
res.status(201).json(contact);
} catch (err) {
res.status(400).json({ error: err.message });
}
});
// Get all contacts
app.get('/contacts', async (req, res) => {
try {
const contacts = await Contact.find();
res.json(contacts);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Get a single contact by ID
app.get('/contacts/:id', async (req, res) => {
try {
const contact = await Contact.findById(req.params.id);
res.json(contact);
} catch (err) {
res.status(404).json({ error: 'Contact not found' });
}
});
// Update a contact by ID
app.put('/contacts/:id', async (req, res) => {
try {
const contact = await Contact.findByIdAndUpdate(req.params.id, req.body, { new: true });
res.json(contact);
} catch (err) {
res.status(400).json({ error: err.message });
}
});
// Delete a contact by ID
app.delete('/contacts/:id', async (req, res) => {
try {
await Contact.findByIdAndDelete(req.params.id);
res.json({ message: 'Contact deleted' });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
Now, our backend is fully set up!
2. Setting Up the Frontend (React 18)
Step 1: Create a new React app
In the project root, create a new React app using the latest version (React 18):
npx create-react-app client
cd client
Step 2: Install Axios for API requests
To interact with our backend API, install Axios:
npm install axios
Step 3: Create Components for Contact Management
Inside the src
folder, create a components
folder to house our React components. We’ll create three components:
- ContactList – to display all contacts
- AddContact – to add a new contact
- EditContact – to edit an existing contact
ContactList.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const ContactList = () => {
const [contacts, setContacts] = useState([]);
useEffect(() => {
axios.get('http://localhost:5000/contacts')
.then(res => setContacts(res.data))
.catch(err => console.error(err));
}, []);
return (
<div>
<h2>Contact List</h2>
{contacts.map(contact => (
<div key={contact._id}>
<p>{contact.name} - {contact.phone}</p>
</div>
))}
</div>
);
}
export default ContactList;
AddContact.js
import React, { useState } from 'react';
import axios from 'axios';
const AddContact = () => {
const [contact, setContact] = useState({ name: '', email: '', phone: '' });
const handleChange = (e) => {
setContact({ ...contact, [e.target.name]: e.target.value });
}
const handleSubmit = (e) => {
e.preventDefault();
axios.post('http://localhost:5000/contacts', contact)
.then(res => console.log('Contact added:', res.data))
.catch(err => console.error(err));
}
return (
<form onSubmit={handleSubmit}>
<input type="text" name="name" placeholder="Name" onChange={handleChange} />
<input type="email" name="email" placeholder="Email" onChange={handleChange} />
<input type="text" name="phone" placeholder="Phone" onChange={handleChange} />
<button type="submit">Add Contact</button>
</form>
);
}
export default AddContact;
EditContact.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { useParams } from 'react-router-dom';
const EditContact = () => {
const [contact, setContact] = useState({ name: '', email: '', phone: '' });
const { id } = useParams();
useEffect(() => {
axios.get(`http://localhost:5000/contacts/${id}`)
.then(res => setContact(res.data))
.catch(err => console.error(err));
}, [id]);
const handleChange = (e) => {
setContact({ ...contact, [e.target.name]: e.target.value });
}
const handleSubmit = (e) => {
e.preventDefault();
axios.put(`http://localhost:5000/contacts/${id}`, contact)
.then(res => console.log('Contact updated:', res.data))
.catch(err => console.error(err));
}
return (
<form onSubmit={handleSubmit}>
<input type="text" name="name" value={contact.name} onChange={handleChange} />
<input type="email" name="email" value={contact.email} onChange={handleChange} />
<input type="text" name="phone" value={contact.phone} onChange={handleChange} />
<button type="submit">Update Contact</button>
</form>
);
}
export default EditContact;
Step 4: Set up React Router
To navigate between the components, install React Router:
npm install react-router-dom
Update src/App.js
to include routing:
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import ContactList from './components/ContactList';
import AddContact from './components/AddContact';
import EditContact from './components/EditContact';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<ContactList />} />
<Route path="/add" element={<AddContact />} />
<Route path="/edit/:
id" element={<EditContact />} />
</Routes>
</Router>
);
}
export default App;
Conclusion
You’ve now built a CRUD application with React 18 and Node.js 20.24. To summarize:
- We built a backend API using Express.js and MongoDB.
- We created a React frontend to manage contact records.
- Axios was used for API requests, and React Router for navigation.
Feel free to extend this application by adding features like validation, pagination, or search functionality!