NodeJSBuilt-in Middleware

Built-in Middleware

Modern Express ships with a small set of built-in middleware — no install required, available as express.something(). They cover the most universal needs: parsing JSON and form bodies, and serving static files. Knowing exactly what each does (and its key options) means you configure them correctly instead of cargo-culting a config from a tutorial.

The built-in set

Middleware

Purpose

express.json()

Parse JSON request bodies → req.body

express.urlencoded()

Parse HTML form bodies → req.body

express.static()

Serve static files from a directory

express.raw()

Body as a raw Buffer (webhooks, binary)

express.text()

Body as a plain string

These were once the separate `body-parser` package
In older Express, JSON/urlencoded parsing came from the third-party `body-parser` module. Express folded them back in as `express.json()` / `express.urlencoded()` — so tutorials that `require('body-parser')` are outdated. Use the built-ins; they *are* body-parser, re-exported.
express.json()

JS
app.use(express.json({
  limit: '1mb',                 // reject bodies larger than this (DoS guard)
  strict: true,                 // only accept arrays/objects (default)
  type: 'application/json',     // which Content-Type to parse
}))

app.post('/data', (req, res) => {
  res.json({ received: req.body })   // req.body is the parsed object
})
Set a `limit` — the default 100kb may be wrong both ways
`express.json()` defaults to a **100kb** body limit. That's too small for some legitimate payloads (bulk imports) and, conversely, you may want it *smaller* on public endpoints to blunt memory-exhaustion attacks. Set `limit` explicitly to match each route's needs. A body over the limit is rejected with `413 Payload Too Large` — handle that in your error middleware.
Only parses requests with a matching Content-Type
`express.json()` only acts on requests whose `Content-Type` is `application/json` (by default) — others pass through untouched, leaving `req.body` empty. So a client that POSTs JSON *without* setting `Content-Type: application/json` will find `req.body` undefined. If the body isn't parsing, check the client's content type first.
express.urlencoded()

JS
// For HTML form submissions (application/x-www-form-urlencoded):
app.use(express.urlencoded({ extended: true, limit: '1mb' }))

// <form method="post"> with name="email" → req.body.email
`extended: true` vs `false` changes how nested data parses
`extended: false` uses Node's `querystring` (flat key/values only); `extended: true` uses the `qs` library, which supports **nested objects and arrays** (`user[name]=Ada` → `{ user: { name: 'Ada' } }`). Pick `true` if your forms send nested fields, `false` for simple flat forms (and a smaller attack surface). There's no universal default to memorize — choose based on your form structure.
express.static()

Covered in depth on its own page — it safely serves files from a directory with correct MIME types, caching, and path-traversal protection:

JS
import { join } from 'node:path'
app.use(express.static(join(import.meta.dirname, 'public'), { maxAge: '1d' }))
express.raw() and express.text()

Two specialized parsers. express.raw() gives the body as a Buffer — essential for webhook signature verification, where you need the exact bytes. express.text() gives a string.

JS
// Webhook: verify a signature over the RAW bytes, so parse as Buffer:
app.post('/webhook',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const valid = verifySignature(req.body, req.get('X-Signature'))  // req.body is a Buffer
    if (!valid) return res.sendStatus(401)
    const event = JSON.parse(req.body.toString('utf8'))
    handle(event)
    res.sendStatus(200)
  })
Webhooks need the RAW body — `express.json()` destroys the bytes you must verify
Signature verification (Stripe, GitHub, etc.) computes a hash over the **exact raw request bytes**. If `express.json()` has already parsed and re-stringified the body, the bytes differ and verification fails. Apply `express.raw()` to the webhook route specifically (not globally), so its body stays a `Buffer` while the rest of your app still gets parsed JSON.
Apply parsers globally vs per-route

JS
// Global JSON parsing for the whole API:
app.use(express.json())

// ...but the webhook route overrides with raw parsing:
app.post('/webhook', express.raw({ type: '*/*' }), webhookHandler)
Scope parsers to where they're needed
You don't have to parse every body the same way. Apply `express.json()` globally for convenience, but attach `express.raw()`/`express.text()` to the specific routes that need different handling. Per-route parsers take precedence for that route. This avoids the all-or-nothing trap and keeps webhooks working alongside a JSON API.
Next
The vast world of middleware from npm — auth, security, logging, and more: [Third-Party Middleware](/nodejs/third-party-middleware).