Building a REST API with Node.js and Express
Put the concepts together by implementing a production-oriented REST API.
Project Structure
Express REST API Structure
A maintainable Node.js API can separate routing, controllers, services, repositories, validation, middleware, configuration, and infrastructure.
Example:
src/
routes/
controllers/
services/
repositories/
middleware/
validators/
errors/
config/
app.js
server.js
The exact structure is less important than maintaining clear boundaries. Avoid turning routes.js into a file containing database queries, authentication, validation, business rules, and response formatting.
const router = express.Router();
router.get('/users/:id', authenticate, userController.getById);
router.post('/users', authenticate, validate(createUserSchema), userController.create);
router.patch('/users/:id', authenticate, validate(updateUserSchema), userController.update);
router.delete('/users/:id', authenticate, userController.remove);Middleware Pipeline
#Middleware forms a pipeline through which requests pass.
Typical middleware includes request IDs, logging, security headers, body parsing, CORS, authentication, validation, authorization, and error handling.
Order matters. Authentication must execute before authorization. Request parsing must occur before body validation. Error middleware must be registered correctly so errors reach it.
Complete CRUD Endpoint Design
CRUD API
A complete users API can expose:
POST /usersGET /usersGET /users/:idPUT /users/:idPATCH /users/:idDELETE /users/:id
Each endpoint should define request validation, authentication requirements, authorization rules, status codes, response schemas, error cases, logging, metrics, and database behavior.
Do not consider an endpoint complete merely because the happy path works.
async function getUser(req, res, next) {
try {
const user = await userService.getById(req.params.id, req.auth);
if (!user) {
return res.status(404).json({
type: 'user-not-found',
title: 'User not found',
status: 404
});
}
res.status(200).json(user);
} catch (error) {
next(error);
}
}Production Hardening
A production API should include environment-based configuration, secrets management, structured logging, request IDs, health endpoints, graceful shutdown, timeouts, validation, rate limiting, secure headers, dependency monitoring, metrics, tracing, automated tests, and deployment automation.
Never place secrets directly in source code. Never expose internal stack traces to clients. Do not assume process-level memory is durable or shared across replicas.
A production-ready API is not defined by the number of endpoints. It is defined by predictable behavior under valid input, invalid input, high load, partial failure, and malicious input.
An API feature is production-ready only when its contract, security, validation, failure behavior, observability, tests, and deployment behavior are understood.