Building RESTful APIs with Node.js and Express.js
Learn to build RESTful APIs using Node.js and Express.js. This guide covers the core concepts of creating, testing, and deploying APIs.
StalkTechie
Author
Building RESTful APIs with Node.js and Express.js
Node.js and Express.js provide a fast, minimal, and scalable framework for building RESTful APIs. This guide walks you through creating APIs, structuring routes, handling requests, and optimizing for performance and security in real-world applications.
Introduction to RESTful APIs
A RESTful API follows the principles of Representational State Transfer (REST). It allows clients to interact with server resources via HTTP methods, keeping operations stateless and standardized.
- GET – Retrieve data
- POST – Create new resources
- PUT – Update existing resources
- DELETE – Remove resources
Setting Up Node.js and Express
# Initialize a new Node.js project
mkdir node-api-example
cd node-api-example
npm init -y
# Install dependencies
npm install express dotenv mongoose cors
// Basic server setup using Express
const express = require("express");
const app = express();
// Middleware
app.use(express.json());
app.use(require("cors")());
// Basic route
app.get("/", (req, res) => {
res.send("Welcome to the Node.js RESTful API!");
});
// Start the server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Creating Modular Routes
It’s best practice to keep routes modular by separating logic into controllers and routes.
// routes/userRoutes.js
const express = require("express");
const router = express.Router();
const userController = require("../controllers/userController");
router.get("/", userController.getAllUsers);
router.post("/", userController.createUser);
router.get("/:id", userController.getUserById);
router.put("/:id", userController.updateUser);
router.delete("/:id", userController.deleteUser);
module.exports = router;
// controllers/userController.js
const User = require("../models/User");
exports.getAllUsers = async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (err) {
res.status(500).json({ message: err.message });
}
};
exports.createUser = async (req, res) => {
const user = new User(req.body);
try {
const savedUser = await user.save();
res.status(201).json(savedUser);
} catch (err) {
res.status(400).json({ message: err.message });
}
};
Integrating MongoDB with Mongoose
// config/db.js
const mongoose = require("mongoose");
mongoose.connect(process.env.MONGO_URI)
.then(() => console.log("MongoDB connected"))
.catch(err => console.error(err));
// models/User.js
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
name: String,
email: String,
age: Number
});
module.exports = mongoose.model("User", userSchema);
Adding Middleware for Error Handling
// middleware/errorHandler.js
module.exports = (err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: "Something went wrong!" });
};
// In app.js
const errorHandler = require("./middleware/errorHandler");
app.use(errorHandler);
Securing the API
APIs are often exposed publicly, so securing them is essential.
- Use environment variables for secrets with
dotenv - Enable CORS for specific domains
- Validate data to prevent injection attacks
- Protect routes with JWT authentication
// Example JWT authentication middleware
const jwt = require("jsonwebtoken");
function authenticateToken(req, res, next) {
const token = req.headers["authorization"]?.split(" ")[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
Optimizing Performance
- Enable gzip compression using
compressionmiddleware - Cache frequent responses with Redis
- Use
helmetfor enhanced security headers - Minimize database calls using indexing and lean queries
// Example using compression and helmet
const compression = require("compression");
const helmet = require("helmet");
app.use(helmet());
app.use(compression());
Testing Your API
You can test API endpoints with tools like Postman or automated tools like Jest and Supertest.
// Example test using Jest and Supertest
const request = require("supertest");
const app = require("../app");
describe("GET /users", () => {
it("should return an array of users", async () => {
const res = await request(app).get("/users");
expect(res.statusCode).toBe(200);
expect(Array.isArray(res.body)).toBe(true);
});
});
Best Practice Summary
- Organize your routes and controllers clearly
- Use environment variables for sensitive data
- Always include error handling middleware
- Secure your endpoints with authentication
- Optimize performance with caching and compression