Express.js is a minimal and flexible Node.js framework for building robust REST APIs that power modern frontend applications.
Express provides essentials to initialize web servers, mount routes, and listen for HTTP traffic.
Require express module, initialize app = express(), and set port.
Separate routes and services, organize middleware, and isolate env variables.
Bind HTTP server using app.listen(PORT) to accept incoming frontend traffic.
const express = require('express');
const app = express();
// Basic Route
app.get('/', (req, res) => {
res.send('Hello Express API!');
});
// Bind Port
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Separation of concerns keeps codebases clean, testable, modular, and maintainable.
Define API endpoints and match HTTP verbs to route handlers.
Handle request/response logic, status codes, and input extraction.
Core business logic, calculations, and data processing.
Authentication, logging, request parsing, and error catching.
Separating Express app configuration (app.js) from server startup (server.js) allows effortless integration testing without binding ports!
Use HTTP methods to map CRUD operations directly to resource-based URL endpoints.
/users, /orders)—never action verbs like (/getUsers, /createOrder).
express.Router()
Isolate route groups into standalone modules and mount them cleanly in main application scripts.
const express = require('express');
const router = express.Router();
router.get('/', getAllUsers);
router.get('/:id', getUserById);
router.post('/', createUser);
module.exports = router;
const express = require('express');
const userRoutes = require('./routes/user.routes');
const orderRoutes = require('./routes/order.routes');
const app = express();
// Prefix all endpoints cleanly
app.use('/api/v1/users', userRoutes);
app.use('/api/v1/orders', orderRoutes);
/api/v1/...) so you can update API logic without breaking existing frontends.
Express provides three primary properties on the req object to receive data from clients.
Identify specific resources from path parameters.
Filter, sort, search, and paginate datasets.
Receive JSON payloads sent during POST/PUT calls.
req.body will be undefined unless you use body-parsing middleware like express.json()!
Middlewares execute before route handlers to process requests, enrich context, or enforce security.
app.use().
next() Control
Custom middlewares accept (req, res, next) to validate requests or inject utility context.
Intercept bad payloads early before reaching controller logic.
Calling next() passes control to the next middleware or route in line.
Halt pipeline immediately and respond if authentication or validation fails.
function authenticateToken(req, res, next) {
const token = req.headers['authorization'];
if (!token) {
return res.status(401).json({ message: 'Unauthorized' });
}
// Enrich request context
req.user = { id: 101, role: 'Admin' };
next(); // Pass to route handler!
}
Predictable response payloads allow frontend clients to handle success and error states cleanly.
{
"success": true,
"message": "Data loaded successfully",
"data": [
{ "id": 1, "name": "Alice", "role": "Admin" }
],
"meta": { "page": 1, "total": 25 }
}
{
"success": false,
"message": "Invalid input format",
"errors": [
{ "field": "email", "issue": "Email is required" }
]
}
(err, req, res, next)
Centralize error handling in a 4-parameter middleware to keep controllers clean and fail safely.
Express recognizes (err, req, res, next) specifically as an error handler.
Never expose raw database error stack traces to clients in production!
// Global Error Middleware (Must have 4 params)
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
// Log diagnostic details on server
console.error(`[Error] ${err.message}`, err.stack);
res.status(statusCode).json({
success: false,
message: err.message || 'Internal Server Error'
});
});
Trace a record creation workflow from user form submit to backend response and state sync.
const res = await fetch('/api/v1/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Ben', role: 'User' })
});
const data = await res.json();
setUsers(prev => [...prev, data.data]);
app.post('/api/v1/users', async (req, res, next) => {
try {
const newUser = await userService.create(req.body);
res.status(201).json({ success: true, data: newUser });
} catch (err) {
next(err);
}
});
Master these rules to build scalable backend APIs and seamless frontend integrations.
/api/v1/...)..env) for port and secrets.Content-Type: application/json or express.json().