Post 4: Building a RESTful API with Express and Node.js
In Post 3, we explored MongoDB and implemented basic CRUD operations. Now, we’ll create a RESTful API with Express and Node.js that serves as the backbone of our MERN stack application, allowing us to manage resources and interact with MongoDB from the client.
What is a RESTful API?
A RESTful API (Representational State Transfer) is an architectural style for building web services. It allows different software systems to communicate over HTTP using standard HTTP methods:
- GET: Retrieve data from the server.
- POST: Send new data to the server.
- PUT: Update existing data on the server.
- DELETE: Remove data from the server.
In this post, we’ll apply these methods to create endpoints for our User resource.
1. Setting Up the Project
First, make sure you have a project structure with backend
and frontend
directories, as we set up in Post 2. We’ll add a few new routes to the backend.
Install Dependencies
In the backend
folder, ensure you have Express and Mongoose installed. If not, install them with:
npm install express mongoose
2. Building Routes in Express
Express makes it simple to define routes and handle HTTP requests. We’ll create routes for basic CRUD operations.
Organize Routes
Inside backend
, create a folder named routes
and add a file userRoutes.js
to handle user-related routes.
// backend/routes/userRoutes.js
const express = require("express");
const router = express.Router();
const User = require("../models/User");
// Get all users
router.get("/", async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Get a user by ID
router.get("/:id", async (req, res) => {
try {
const user = await User.findById(req.params.id);
if (!user) return res.status(404).json({ message: "User not found" });
res.json(user);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Create a new user
router.post("/", async (req, res) => {
const user = new User(req.body);
try {
const newUser = await user.save();
res.status(201).json(newUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// Update a user by ID
router.put("/:id", async (req, res) => {
try {
const updatedUser = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
res.json(updatedUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// Delete a user by ID
router.delete("/:id", async (req, res) => {
try {
await User.findByIdAndDelete(req.params.id);
res.json({ message: "User deleted" });
} catch (error) {
res.status(500).json({ message: error.message });
}
});
module.exports = router;
Update the Server Entry File
In index.js
, add middleware to handle JSON data and register the userRoutes
we just created.
// backend/index.js
const express = require("express");
const mongoose = require("mongoose");
const dotenv = require("dotenv");
dotenv.config();
const app = express();
const PORT = process.env.PORT || 5000;
// Connect to MongoDB
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log("Connected to MongoDB"))
.catch(err => console.error("MongoDB connection error:", err));
app.use(express.json()); // Middleware to parse JSON
app.use("/api/users", require("./routes/userRoutes")); // Register user routes
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
3. Testing the API
With the API routes set up, you can test each endpoint using Postman or a similar API client.
-
GET
http://localhost:5000/api/users
: Retrieves a list of all users. -
GET
http://localhost:5000/api/users/:id
: Retrieves a specific user by ID. -
POST
http://localhost:5000/api/users
: Creates a new user by sending JSON data (e.g.,{ "name": "Alice", "email": "alice@example.com", "password": "password" }
). -
PUT
http://localhost:5000/api/users/:id
: Updates an existing user by sending JSON data. -
DELETE
http://localhost:5000/api/users/:id
: Deletes a specific user by ID.
4. Integrating Error Handling and Validation
To make the API more robust, we can add validation and error handling. Here’s a basic example of validating input on user creation.
router.post("/", async (req, res) => {
const { name, email, password } = req.body;
if (!name || !email || !password) {
return res.status(400).json({ message: "All fields are required" });
}
try {
const user = new User({ name, email, password });
await user.save();
res.status(201).json(user);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
Next Steps
In Post 5, we’ll create the frontend UI with React to interact with this API, retrieving and managing user data visually. Our application will start taking shape as we build out the user interface and connect it to the backend API we created here.
Top comments (0)