Third-party Middleware

intermediate express middleware npm

Express ships with a tiny core. Almost every real app stacks 4-6 third-party middleware packages to handle the boring stuff — parsing bodies, logging, security headers, CORS. In simple language: these are pre-built lego pieces we drop into app.use() and forget about.

Think of the request flowing through layers of an onion. Each middleware peels a layer, adds something, then hands off to the next.

Incoming Request
helmet  ·  security headers
cors  ·  allow cross-origin
morgan  ·  log the request
compression  ·  gzip the response
express.json  ·  parse body
cookie-parser  ·  read cookies
your route handler

The usual suspects

cors — adds Access-Control-Allow-* headers so browsers on different origins can call our API. Without it, a frontend on localhost:3000 calling an API on localhost:4000 gets blocked.

helmet — sets a bundle of security headers (CSP, HSTS, X-Frame-Options, etc.). One line, instant hardening.

morgan — logs every request: method, URL, status, response time. Great in dev, in prod we usually pipe it to a file or a real logger.

compression — gzips responses. Saves bandwidth on JSON payloads and HTML. The browser handles decompression.

cookie-parser — populates req.cookies from the Cookie header. Without it, req.cookies is undefined.

body-parser — parses JSON / URL-encoded request bodies into req.body. Since Express 4.16, this is built in as express.json() and express.urlencoded(), so we rarely install body-parser directly anymore.

Install and wire them up

npm install cors helmet morgan compression cookie-parser
const express = require("express");
const cors = require("cors");
const helmet = require("helmet");
const morgan = require("morgan");
const compression = require("compression");
const cookieParser = require("cookie-parser");

const app = express();

// Security first — set headers before anything else
app.use(helmet());

// Allow our frontend to call us
app.use(cors({ origin: "https://app.example.com", credentials: true }));

// Log every request in dev, "combined" format in prod
app.use(morgan(process.env.NODE_ENV === "production" ? "combined" : "dev"));

// Gzip responses
app.use(compression());

// Parse JSON bodies (built into Express now)
app.use(express.json({ limit: "1mb" }));
app.use(express.urlencoded({ extended: true }));

// Parse cookies
app.use(cookieParser(process.env.COOKIE_SECRET));

app.get("/api/users/:id", (req, res) => {
  console.log(req.cookies.session);   // from cookie-parser
  console.log(req.body);              // from express.json
  res.json({ id: req.params.id, name: "Manish" });
});

app.listen(3000);

Order matters

Helmet and cors go near the top — they set headers, and headers need to be set before res.send() flushes. Body parsers go before routes. Error middleware always goes last (covered in the next note).

The only thing to remember: request flows top-to-bottom, so whatever a route needs must be app.use()’d before that route is defined.

When to skip

  • Tiny internal service behind a VPN? Skip cors and helmet.
  • Pure JSON API? Skip view engines, skip express.urlencoded().
  • Behind nginx/Caddy with gzip already? Skip compression.

Don’t add middleware blindly — each one costs a few microseconds per request and adds an attack surface. Pick what we actually need.