All Posts
Node.js

Building a Production-Ready REST API with Node.js and Express

April 28, 2025

Plenty of tutorials show a single index.js with every route crammed in. Here's the structure I actually reach for once an API needs to survive real traffic and a growing team.

Project Structure

src/
  routes/
    users.route.js
  controllers/
    users.controller.js
  middleware/
    errorHandler.js
    validate.js
  app.js
  server.js

Centralized Error Handling

// middleware/errorHandler.js
function errorHandler(err, req, res, next) {
  const status = err.statusCode || 500;
  res.status(status).json({ error: err.message || 'Internal Server Error' });
}
module.exports = errorHandler;

Every route wraps async logic in a try/catch and calls next(err) instead of handling errors inline. One handler, registered last in app.js, catches everything.

Input Validation Before It Hits Business Logic

const { z } = require('zod');

const createUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2),
});

function validate(schema) {
  return (req, res, next) => {
    const result = schema.safeParse(req.body);
    if (!result.success) return res.status(400).json({ error: result.error.issues });
    req.body = result.data;
    next();
  };
}

Non-Negotiables for Production

  • Rate limiting on public endpoints (express-rate-limit)
  • Helmet for sensible default security headers
  • Structured logging (pino or winston) instead of console.log
  • Graceful shutdown handling on SIGTERM so in-flight requests finish before the process exits

None of this is exotic — it's the boring, repeatable checklist that turns a demo API into one you can actually put behind a load balancer.