Chapter 14 of 18 · Node.js for Beginners — Complete Free Tutorial

Middleware Explained

If routes are the destinations, middleware is the road network. The Node.js for Beginners series now covers Express's central idea: a pipeline of functions that every request travels through, each one doing a single job and passing control on with next(). Once this clicks, every Express feature — and every Node framework built in its image — becomes predictable.

The pipeline

Middleware are functions with the signature (req, res, next). They run in registration order, before any route handler, and can end the response or hand off to the next function.

app.use((req, res, next) => {
  console.log(`${new Date().toISOString()} ${req.method} ${req.url}`);
  next();                       // without this, the request hangs forever
});

app.use((req, res, next) => {
  req.startedAt = Date.now();
  next();
});

app.get('/api/ping', (req, res) => {
  res.json({ pong: true, tookMs: Date.now() - req.startedAt });
});
curl -s http://localhost:3000/api/ping

Expected output: the console logs a timestamped line for every request, and the client receives {"pong":true,"tookMs":0}. Think of a request as entering a tunnel of functions: each either replies or waves the request onward; the response travels back out the way it came.

The five kinds

KindRegistered withExample
Application-levelapp.use(fn)Logging, request ids, feature flags
Built-inapp.use(express.json())Body parsing, static files, URL encoding
Router-levelrouter.use(fn)Applied only to /api routes
Third-partyapp.use(cors())cors, helmet, rate limiters (chapter 17)
Error-handlingapp.use((err, req, res, next) => …)Central failure translation (four arguments)

Built-ins in practice

app.use(express.json());                  // JSON bodies → req.body
app.use(express.urlencoded({ extended: false }));  // HTML form bodies
app.use(express.static('public'));        // files from ./public
app.use('/api', apiRouter);               // mount a router under /api

Expected output: after these four lines, a POST with Content-Type: application/json arrives pre-parsed, a classic form submission lands as fields, the homepage HTML is served without a single route, and all API routes are grouped behind a prefix. Mounting with app.use('/api', router) keeps route files short and namespacing consistent.

Custom middleware worth writing

async function requireToken(req, res, next) {
  const token = req.get('Authorization')?.replace('Bearer ', '');
  if (token !== process.env.API_TOKEN) {
    return res.status(401).json({ error: 'missing or invalid token' });
  }
  next();
}

// usage: protect only writes
app.post('/api/todos', requireToken, createTodo);

Expected output: a POST without the header replies 401 {"error":"missing or invalid token"}; with the correct bearer token it proceeds to the handler. Notice the pattern: middleware decides whether the request may continue, routes decide what it means. Authentication belongs here exactly once, reused by every protected route.

Error-handling middleware

A function with four parameters — (err, req, res, next) — is Express's failure channel. Call next(err) anywhere and the chain skips ahead to the nearest error handler.

app.get('/api/boom', (req, res, next) => {
  next(new Error('database exploded'));   // jump straight to error middleware
});

app.use((err, req, res, next) => {
  console.error(err.stack);
  const status = err.status ?? 500;
  res.status(status).json({
    error: status === 500 ? 'internal server error' : err.message,
  });
});

Expected output: /api/boom logs the stack server-side and answers {"error":"internal server error"} with 500 — details never leak to the client. Express 5 additionally catches rejected promises from async handlers automatically; on Express 4 you must wrap them or forward rejections with next(err) yourself.

Common mistakes

  • Forgetting next() — the request stalls until the client times out; the classic first middleware bug.
  • Error handler in the wrong position — it must be registered after the routes it should cover.
  • Three-argument "error" middleware — Express identifies error handlers by arity; four parameters is not optional.
  • One middleware doing five jobs — split parsing, auth and logging so failures are diagnosable.
  • Swallowing errors — catching and not responding or forwarding leaves the client hanging and the bug invisible.

Practice checklist

  1. Write a logger middleware and confirm it fires for static files as well as API routes.
  2. Attach req.startedAt in middleware and report duration from a route.
  3. Protect a route with a bearer-token check that answers 401 on failure.
  4. Add a four-parameter error handler and trigger it with next(err).
  5. Register a second error handler before your routes and prove it never runs.

Routes, parameters and middleware are the full vocabulary — time to speak sentences. Next: Building a REST API.

Quiz: Middleware Explained

5 questions · 70% to pass

Frequently Asked Questions

Node.js is a runtime that executes JavaScript outside the browser. It combines Google's V8 engine with APIs for files, networks, processes and streams, plus a package ecosystem (npm). You use it to build servers, command-line tools, APIs and anything that needs JavaScript running on a machine rather than in a page.

JavaScript is the language; Node.js is one environment that runs it. In the browser the language comes with a DOM, events and window APIs — in Node those are replaced by the file system, network and process modules. Syntax, functions, arrays, promises and async/await are identical, so your JavaScript knowledge transfers directly.

Browser code is sandboxed: it cannot read your files or open arbitrary ports. Node code has full machine access, which is why it is used for servers and tools but also why you never run untrusted code in it. There is no document or window in Node — the equivalents are global, process and modules.

npm is JavaScript's package manager and public registry. package.json describes your project: name, entry point, dependencies with version ranges, and scripts such as npm run dev. Installing a package copies it into node_modules and records the exact intent in package-lock.json so every machine installs identical versions.

Express is a minimal web framework for Node: a few thousand lines that give you routing, middleware, request parsing and static files on top of the raw HTTP server. It is the most common starting point for REST APIs and the backbone of this tutorial's project chapters.

Both work. CommonJS (require/module.exports) is the classic Node format; ES modules (import/export) are the standard and activated in a package.json with "type": "module". Modern projects favour ESM for browser compatibility, but you will read both in existing code, so this course teaches each.

Middleware are functions that run between the incoming request and the final response, in order: body parsing, logging, authentication, rate limits, error handling. Each receives the request, response and next function and can respond itself or pass control along — Express's pipeline model in one sentence.

A JSON file is perfect for learning CRUD: no setup, readable data, easy to reset. The moment you need concurrent writes, querying or multiple users, move to a real database — MySQL/PostgreSQL for relational data or MongoDB for documents. The storage chapter isolates persistence behind one module so swapping it later is straightforward.

A REST API exposes resources over HTTP using verbs on URLs: GET /api/todos lists, POST creates, PUT or PATCH updates, DELETE removes, and everything answers with JSON plus correct status codes (200, 201, 400, 404, 500). It is stateless — each request carries its own authentication — which makes it easy to cache, scale and consume from any frontend.

Node apps are long-running processes, not static files: deploy them to a Node-friendly host (a VPS, Render, Railway or Fly.io), keep secrets in environment variables, terminate HTTPS at a reverse proxy or platform edge, and send logs somewhere durable. The final chapter covers the full checklist including health checks and graceful shutdown.