cheat sheet
body-parser
Package-level reference for body-parser on npm — JSON, urlencoded, raw and text middleware, webhook signatures, and Express 4.16+ bundling.
body-parser
What it is
body-parser is the canonical Node middleware for parsing HTTP request bodies into req.body. It exposes four parsers — json, urlencoded, raw, and text — each as an Express-compatible middleware factory. Since Express 4.16 (released 2017), the same parsers are exposed under express.json(), express.urlencoded(), express.raw(), and express.text(). The standalone body-parser package remains the right install only for non-Express servers (Connect, Polka, raw Node) or when you want to mount a parser before Express's bundled version is loaded.
Reach for body-parser directly when you need fine-grained limits, custom verify callbacks (e.g. preserving raw bytes for webhook signatures), or when running on a non-Express stack.
Install
npm install body-parser
Output: added body-parser to dependencies
pnpm add body-parser
Output: added 1 package, linked from store
yarn add body-parser
Output: added body-parser
bun add body-parser
Output: installed body-parser
Types ship in-tree as of @types/body-parser for legacy projects; modern Express projects get the typings via @types/express directly.
Versioning & Node support
Current line is body-parser@1.x (the package has stayed on the 1.x major for years; semantic versioning is loose for a mid-2010s Express ecosystem package).
body-parser@1— Node 0.10+ (effective floor today is whatever Express supports, currently Node 18+ forexpress@5). The 1.x line continues to receive security patches.body-parser@2— early-2025 release that aligned the package with Express 5's stricter defaults. Adoption is still gradual; Express 4 projects can stay on 1.x.
Pin minor in production. Express 4.16+ bundles a compatible copy as express.json() / express.urlencoded() — most apps don't need a separate install at all.
Package metadata
- Maintainer: Express TC (
expressjs/body-parser) - Project home: github.com/expressjs/body-parser
- Docs: github.com/expressjs/body-parser#readme
- npm: npmjs.com/package/body-parser
- License: MIT
- First released: 2014
- Downloads: ~25 million+ weekly downloads.
Peer dependencies & extras
No peer-deps. Transitive dependencies include bytes (parse "10mb" strings), content-type, debug, iconv-lite (charset decoding), raw-body, qs or querystring for urlencoded parsing, on-finished, and type-is.
Commonly paired with:
express— bundles a copy as of 4.16+multer— multipart/form-data (NOT covered by body-parser; multipart needs a separate parser)cookie-parser— sibling middleware for cookiesexpress-validator— validation on top of parsed bodieszod/valibot/joi— schema validation against parsed bodieshelmet— security headers; pair with body-parser limitscors— CORS handling
Alternatives
| Approach | Trade-off |
|---|---|
express.json() / .urlencoded() | Bundled in Express 4.16+. Same package under the hood. Default for Express apps. |
fastify's built-in parsers | Fastify parses JSON / urlencoded itself; no body-parser needed. |
hono.req.json() / .parseBody() | Hono is async-pull; no middleware install required. |
koa-bodyparser | Koa equivalent. Same authors / similar API. |
raw-body | The lower-level package body-parser is built on. Use when you need just the bytes without parsing. |
multer / busboy | Multipart/form-data parsing (file uploads). Body-parser does NOT handle multipart. |
Real-world recipes
JSON middleware
The most common parser. Reads a JSON body, parses into req.body. Default size limit is 100 KB.
import express from "express";
import bodyParser from "body-parser";
const app = express();
app.use(bodyParser.json({ limit: "1mb" }));
app.post("/users", (req, res) => {
console.log(req.body);
res.json({ ok: true });
});
app.listen(3000);
Output: posting {"email":"alice@example.com"} makes req.body.email available. Bodies over 1 MB return PayloadTooLargeError.
Inside Express, prefer app.use(express.json({ limit: "1mb" })) — identical behaviour, no extra dep.
URL-encoded middleware
For HTML form posts. extended: true uses the qs parser (supports nested objects); extended: false uses querystring (flat keys only).
import express from "express";
import bodyParser from "body-parser";
const app = express();
app.use(bodyParser.urlencoded({ extended: true, limit: "100kb" }));
app.post("/contact", (req, res) => {
console.log(req.body);
res.send("thanks");
});
app.listen(3000);
Output: posting name=Alice&tags[]=draft&tags[]=urgent produces { name: "Alice", tags: ["draft", "urgent"] }. With extended: false you'd get strings only.
For pure-API services that never accept HTML forms, omit this middleware entirely — there's no reason to parse what you never receive.
Raw body for webhook signatures
Many webhook providers (Stripe, Slack, GitHub) sign the raw request body. JSON parsing breaks signature verification because key order and whitespace change. Use bodyParser.raw (or verify callback on json) to preserve the raw bytes.
import express from "express";
import bodyParser from "body-parser";
import crypto from "node:crypto";
const app = express();
app.post("/webhook",
bodyParser.raw({ type: "application/json" }),
(req, res) => {
const signature = req.headers["x-signature"];
const expected = crypto
.createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(req.body)
.digest("hex");
if (signature !== expected) return res.status(401).send("bad sig");
const event = JSON.parse(req.body.toString("utf8"));
console.log(event);
res.sendStatus(204);
}
);
app.listen(3000);
Output: signature verified against the exact bytes; mismatch returns 401. req.body is a Buffer.
Alternative: use bodyParser.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }) to retain raw bytes alongside parsed JSON.
Per-route parsers with strict size limits
Mount parsers per route to enforce different limits — bulk imports may allow 10 MB, write endpoints 10 KB. Per-route parsers also reduce attack surface: routes that never accept bodies don't parse them.
import express from "express";
import bodyParser from "body-parser";
const app = express();
const smallJson = bodyParser.json({ limit: "10kb" });
const largeJson = bodyParser.json({ limit: "10mb" });
app.post("/api/users", smallJson, (req, res) => res.json({ id: "u-1" }));
app.post("/api/imports", largeJson, (req, res) => res.json({ ok: true }));
app.get("/api/health", (req, res) => res.json({ ok: true })); // no parser at all
app.listen(3000);
Output: /api/users rejects bodies >10 KB; /api/imports allows up to 10 MB; /api/health doesn't even attempt to parse.
Production deployment
Size limits are mandatory
Without limit, a malicious client can post gigabytes of JSON and exhaust memory. The default 100 KB JSON limit is sane for most APIs; raise only where needed.
app.use(bodyParser.json({ limit: "100kb" }));
Output: any Content-Length over 100 KB returns 413 Payload Too Large before allocation begins.
Strict content-type matching
type accepts a string, array, or function. Restrictive content-type filters prevent a JSON parser from accidentally trying to parse application/octet-stream.
app.use(bodyParser.json({ type: "application/json", strict: true }));
Output: non-JSON content types pass through unparsed; strict: true rejects primitives like "hello" (top-level must be object or array).
Behind a reverse proxy
Body parsers care about Content-Length and Transfer-Encoding. If your proxy buffers requests (Cloudflare, AWS ALB) the size limit applies to the proxied length, which matches what your app actually receives. If you stream uploads, the limit may behave differently — test with curl --data-binary @largefile.
Mount order
Mount body-parser before any route that consumes req.body. Mounting after the route means req.body is undefined because the middleware never runs.
app.use(bodyParser.json()); // before routes
app.post("/x", handler);
Memory pressure
Body parsing copies the full request into memory before invoking the handler. For multi-megabyte payloads (file uploads, bulk imports), consider streaming instead — multer for multipart, or read req directly as a stream.
Performance tuning
- Use the bundled
express.json()instead of body-parser when on Express. One fewer dep; identical hot path. - Smaller
limitvalues fail faster. Express short-circuits onContent-Lengthbefore allocation. inflate: falsedisables gzip/deflate decompression on bodies. Defaulttrue; set false if your proxy already decompresses.- Avoid
extended: trueurlencoded if you don't need nesting.qsis slower thanquerystring. - Per-route mounting over global. Don't parse what you don't read.
- Skip body-parser entirely for routes without bodies (GET, HEAD, DELETE without body).
revivercallbacks on JSON are slow. If you need post-parsing transforms, do them in the handler.
Version migration guide
body-parser@1 is the long-stable line
Most production projects are on a recent 1.x patch release. The 1.x line continues to receive security fixes.
Migrating from standalone to Express-bundled
If you're on Express 4.16+ and using body-parser separately:
// before
import bodyParser from "body-parser";
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// after
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
Then npm uninstall body-parser. No behaviour change.
body-parser@2
Aligned defaults with express@5:
strict: trueis the default for JSON (top-level must be object/array).extended: falseis the default for urlencoded (wastruein 1.x for a long time, thenfalse, then back — this finally settles it).- Stricter error envelopes on malformed bodies.
- Node 18+ floor.
Migration is mostly checking defaults; opt back into old behaviour explicitly if your tests fail.
Security considerations
- Always set
limit. Unlimited bodies are a denial-of-service vector. 100 KB is a safe default for most APIs. - Strict content-type. Only parse the content types you expect. A
type: "*/*"catch-all accepts attacker-supplied content types. extended: trueurlencoded usesqs, which historically had prototype-pollution CVEs. Keepqsupdated vianpm audit; considerextended: falsefor pure flat forms.- Preserve raw bytes for signed webhooks. JSON parsing alters whitespace; signatures break.
- Don't trust
Content-Length. body-parser checks it, but chunked encoding can bypass declared lengths. Thelimitenforces actual consumed bytes. - No multipart support. body-parser does NOT parse
multipart/form-data. Pair withmulterand apply equivalent size limits there. - JSON null-prototype.
JSON.parsereturns objects withObject.prototype— careless property access can hit prototype-polluted globals. UseObject.hasOwnfor untrusted input. - Reject malformed bodies firmly. Express 5 + body-parser 2 return 400 on syntax errors. Older defaults silently set
req.body = {}— verify your version and behaviour. - Audit
verifycallbacks. They run before parsing and can throw to reject the request. A heavyverifyadds per-request cost.
Testing & CI integration
For unit tests, use supertest against an Express app — body-parser participates in the full pipeline without binding a port.
import { test, expect } from "vitest";
import express from "express";
import request from "supertest";
import bodyParser from "body-parser";
const app = express();
app.use(bodyParser.json({ limit: "10kb" }));
app.post("/echo", (req, res) => res.json(req.body));
test("parses small JSON", async () => {
const res = await request(app).post("/echo").send({ ok: true });
expect(res.status).toBe(200);
expect(res.body).toEqual({ ok: true });
});
test("rejects oversized payload", async () => {
const big = { x: "a".repeat(20_000) };
const res = await request(app).post("/echo").send(big);
expect(res.status).toBe(413);
});
Output: small JSON returns 200; oversized returns 413 Payload Too Large.
For webhook signature paths, capture the raw body in a test fixture and assert verification logic against known good and known bad signatures.
Ecosystem integrations
| Tool | Role |
|---|---|
express | Bundles body-parser as express.json() / express.urlencoded() since 4.16. |
express-validator | Validation layer over parsed bodies. |
zod / valibot / joi | Schema validators applied to req.body. |
multer | multipart/form-data parser. Complements body-parser. |
morgan | Request logger; body parsing happens before logging. |
helmet | Sets security headers; orthogonal to body-parser. |
cors | CORS middleware; mount before body-parser to avoid parsing OPTIONS bodies. |
connect | Body-parser works in raw Connect apps too. |
Troubleshooting common errors
PayloadTooLargeError: request entity too large — body exceeded limit. Raise the limit if legitimate, or reject upstream.
SyntaxError: Unexpected token in JSON — malformed JSON body. Inspect the raw body with a temporary verify callback; reject with a sanitized error.
req.body is undefined — middleware not mounted before the route. Move app.use(bodyParser.json()) above route registrations.
req.body is {} even with a valid JSON body — content-type mismatch. body-parser only parses bodies whose Content-Type matches its type option. Check req.headers["content-type"].
Webhook signature fails — JSON parsing altered the body. Switch to bodyParser.raw or capture raw bytes via verify.
UnexpectedTokenError for empty body — strict: true rejects empty / non-object payloads. Set strict: false if you intentionally accept primitives.
Slow handler with large payloads — body parsing is synchronous after the body arrives. The middleware itself is fast; the handler doing JSON.parse twice is slow. Audit double-parsing.
HPE_HEADER_OVERFLOW — Node rejects huge headers before body-parser sees them. Not a body-parser issue; configure --max-http-header-size if necessary.
Cannot read properties of undefined (reading '...') in handler — accessing req.body.foo when no body was sent. Validate or guard with optional chaining.
Unsupported Content-Encoding — gzip body but inflate: false. Re-enable inflation.
When NOT to use this
- You're on Express 4.16+ — use the bundled
express.json()/express.urlencoded(). Same code, one fewer dep. - You're on Fastify / Hono / Koa. Each has its own body parsing. body-parser is Express-shaped.
- You need multipart/form-data. Use
multerorbusboy. body-parser explicitly doesn't handle multipart. - You stream large uploads. Body-parser buffers the full body in memory. For >10 MB uploads, stream directly from
req. - Edge runtimes. Cloudflare Workers / Vercel Edge use Web
Request.json()/Request.formData()— no middleware required. - You only support
GETrequests. No bodies to parse.
See also
- npm: express — host framework for body-parser
- Concept: http — request body semantics and Content-Type
- Concept: json — parsing and validation