Node.js allows you to run JavaScript on the server. Express is the most popular, minimal web framework for Node.js. Together, they make it incredibly fast to build robust REST APIs.
Step 1 — Initializing a Node.js Project
First, you need to create a project directory and initialize it with npm (Node Package Manager).
mkdir my-express-api
cd my-express-api
# Initialize package.json
npm init -y
# Install Express and a development dependency (nodemon) for auto-restarts
npm install express
npm install --save-dev nodemonStep 2 — Building Your First Server
Let's create a server.js file and set up a basic Express app that listens on a port.
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
// Basic GET route
app.get('/', (req, res) => {
res.send('Hello, World! Express server is running.');
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});Step 3 — Express Middleware
Middleware functions have access to the request object (req), the response object (res), and the next middleware function (next). They form the core of Express architecture.
// Built-in middleware to parse incoming JSON payloads (e.g. POST request bodies)
app.use(express.json());
// Custom logging middleware
app.use((req, res, next) => {
console.log(`${req.method} request to ${req.url}`);
next(); // Pass control to the next middleware/route handler
});Types of Middleware
- Application-level: Runs for every request (e.g., logging, CORS).
- Router-level: Bound to specific routes (e.g., authentication checks).
- Error-handling: Has 4 arguments (err, req, res, next) and catches unhandled exceptions.
Step 4 — Routing & Request Parameters
Express routing maps HTTP methods and URLs to specific handler functions.
const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
// GET all users
app.get('/api/users', (req, res) => {
res.json(users);
});
// Route Parameters (Dynamic Segments)
app.get('/api/users/:id', (req, res) => {
const userId = parseInt(req.params.id);
const user = users.find(u => u.id === userId);
if (!user) return res.status(404).json({ error: 'User not found' });
res.json(user);
});
// Query Parameters
// Example: /api/search?q=express
app.get('/api/search', (req, res) => {
const query = req.query.q;
res.json({ searchResultFor: query });
});Step 5 — Handling POST Requests
To accept data from clients (like forms or JSON payloads), you handle POST requests. Remember to use express.json() middleware first!
app.post('/api/users', (req, res) => {
const newUser = req.body;
// Basic validation
if (!newUser.name) {
return res.status(400).json({ error: 'Name is required' });
}
newUser.id = users.length + 1;
users.push(newUser);
// Return 201 Created and the new object
res.status(201).json(newUser);
});Step 6 — Centralized Error Handling
Instead of returning errors manually everywhere, use Express's centralized error-handling middleware. This must be the very last app.use() in your file.
// A route that throws an error
app.get('/api/broken', (req, res, next) => {
const error = new Error('Database connection failed');
error.status = 500;
next(error); // Passes the error to the error handler
});
// Global Error Handler
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: {
message: err.message || 'Internal Server Error'
}
});
});