Node.js Express Backend Development Guide: Routing, Middleware and Error Handling

In the JavaScript ecosystem, Node.js + Express is one of the most popular stacks for building backend APIs. Express is a minimal yet flexible web framework: instead of making decisions for you, it hands you control over how requests arrive, how they are processed, and how responses are returned. Based on the official Node.js and Express documentation, this guide covers the four core building blocks: routing, middleware, error handling, and project structure.

Setup and Project Initialization

Make sure Node.js (an LTS release is recommended) is installed, then initialize your project:

mkdir my-api && cd my-api
npm init -y
npm install express

npm init -y generates a package.json, and require('express') creates an application instance. Express does not enforce a directory layout, but modularizing your code makes long-term maintenance much easier.

Routing: Entry Points and Parameters

Routing defines how an application's endpoints respond to client requests. Express route methods correspond to HTTP methods — for example, app.get() handles GET and app.post() handles POST:

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello World');
});

app.get('/users/:userId', (req, res) => {
  res.json({ userId: req.params.userId });
});

app.listen(3000);

The :userId segment is a named route parameter captured into req.params. When several HTTP methods share a path, chain them with app.route(); to split a group of related routes into a separate module, use express.Router, which the docs describe as a "mini-app":

// routes/users.js
const router = express.Router();
router.get('/', (req, res) => res.json({ users: [] }));
module.exports = router;

Then mount it in the main app: app.use('/api/users', usersRouter). One file per resource keeps things clear as your route surface grows.

Middleware: The Request Pipeline

Middleware is Express's pipeline mechanism. Each middleware function receives req, res, and next; it can run any logic, then call next() to hand control to the next middleware. The key rule is that load order equals execution order, and if a middleware does not terminate the request-response cycle, it must call next() or the request hangs forever.

The official docs show the classic custom logger example:

const myLogger = function (req, res, next) {
  console.log('LOGGED');
  next();
};
app.use(myLogger);

Common third-party middleware includes express.json() for parsing JSON bodies, cors for cross-origin requests, and helmet for hardening HTTP headers. If you build a decoupled frontend or a mobile app, CORS is essentially mandatory.

Error Handling: A Unified Safety Net

Express recognizes an error-handling middleware by its extra err parameter:

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(err.status || 500).json({ error: err.message });
});

It must be placed after all routes. Combined with a 404 fallback route, this forms a complete loop: unmatched requests return 404, and anything else flows into the error handler. In Express 5, rejected Promises are automatically forwarded to the error middleware, so prefer async handlers to avoid hand-written try/catch blocks.

String the 404 fallback, request logging, and a unified error handler together and you have a minimal runnable app (install morgan first):

const express = require('express');
const app = express();

app.use(express.json());
app.use(morgan('combined'));

app.get('/health', (req, res) => res.json({ ok: true }));

app.use((req, res, next) => {
  res.status(404).json({ error: 'Not Found' });
});

app.use((err, req, res, next) => {
  console.error(err);
  res.status(err.status || 500).json({ error: err.message });
});

app.listen(3000);

Order matters: express.json() and logging go first, business routes in the middle, and the 404 fallback plus error handler last. The most common beginner mistake is registering the error middleware before the routes, which means errors never reach it.

If you start from scratch, go straight to Express 5. The main differences from 4 center on async error handling:

Dimension Express 4 Express 5
Async errors Manual try/catch Auto-catches Promise rejections
Path syntax path-to-regexp 0.x path-to-regexp 8.x
Wildcards * /*splat and similar
Ecosystem Compatible Compatible with mainstream middleware

When upgrading an older project, the wildcard route syntax change is the most common trap — run your test suite before migrating.

Recommended Project Structure

  • app.js: creates the Express instance and mounts global middleware and routes;
  • server.js: starts the listener (app.listen);
  • routes/: routes split by resource;
  • controllers/: business logic;
  • middleware/: auth, logging, validation;
  • models/ or services/: the data access layer.

This layering keeps every file's responsibility focused and makes testing easier. For a complete runnable example, see our Node.js REST API Example.

A Real Scenario: A Three-Person Product API

Picture a three-person team building a REST API for a SaaS — users, orders, and payment callbacks. Their approach: routes/ splits users.js and orders.js by resource, middleware/ holds JWT auth and input validation, services/ contains business logic, and every async route uses the async style. The two pain points that bit them in production: a middleware that forgot next(), leaving requests spinning as if swallowed (only caught via request logs); and CORS that worked fine locally but got blocked by the browser in production. The first is caught by code review plus request logging; the second should be configured with the real production domain at project initialization, not remembered after deployment.

16IDC perspective

For solo developers and small teams, Express has the gentlest learning curve: a single file can serve endpoints, and you can go live on Vercel, Render, or a cloud server. Build solid API fundamentals first with Website API Integration Basics, and plan your API Error Handling and Retry Strategy before launch. More backend content lives in the Backend Integration category.

Reference: Express routing guide https://expressjs.com/en/guide/routing.html · Express middleware https://expressjs.com/en/guide/using-middleware.html · Express error handling https://expressjs.com/en/guide/error-handling.html
Source: https://expressjs.com/en/guide/routing.html