Getting started
Installation
Using Express Built-in (Recommended)
# Express 4.16.0+ includes body-parser
npm install express
No separate installation needed for Express 4.16.0+.
Standalone Package
npm install body-parser
Use when you need body-parser outside Express or need specific versions.
Quick Start (Express Built-in)
const express = require("express");
const app = express();
// Parse JSON bodies
app.use(express.json());
// Parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));
app.post("/api/users", (req, res) => {
console.log(req.body); // Parsed body
res.json(req.body);
});
Quick Start (Standalone)
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
// Parse JSON bodies
app.use(bodyParser.json());
// Parse URL-encoded bodies
app.use(bodyParser.urlencoded({ extended: true }));
app.post("/api/users", (req, res) => {
console.log(req.body);
res.json(req.body);
});
Parsers
JSON Parser
// Express built-in
app.use(express.json());
// Standalone
app.use(bodyParser.json());
// With options
app.use(
express.json({
limit: "10mb",
type: "application/json",
}),
);
Parses application/json Content-Type.
URL-encoded Parser
// Express built-in (qs library)
app.use(express.urlencoded({ extended: true }));
// Querystring library (simple)
app.use(express.urlencoded({ extended: false }));
// Standalone
app.use(bodyParser.urlencoded({ extended: true }));
Parses application/x-www-form-urlencoded Content-Type.
| Option | Description |
|---|---|
extended: true |
Uses qs library (nested objects) |
extended: false |
Uses querystring (flat) |
Raw Parser
// Express built-in
app.use(express.raw({ type: "application/octet-stream" }));
// Standalone
app.use(bodyParser.raw({ type: "application/octet-stream" }));
// Custom type
app.use(express.raw({ type: "application/vnd.custom-type" }));
Returns body as Buffer. Useful for binary data.
Text Parser
// Express built-in
app.use(express.text({ type: "text/plain" }));
// Standalone
app.use(bodyParser.text({ type: "text/plain" }));
// Custom type
app.use(express.text({ type: "text/*" }));
Returns body as string. Parses text-based Content-Types.
Configuration Options
Common Options
| Option | Default | Description |
|---|---|---|
limit |
'100kb' |
Max body size |
type |
Varies | MIME type(s) to parse |
verify |
undefined |
Custom verification function |
inflate |
true |
Enable gzip/deflate |
JSON Options
app.use(
express.json({
limit: "10mb", // Max body size
strict: true, // Only arrays/objects
type: "application/json", // MIME type
verify: (req, res, buf, encoding) => {
// Custom verification
},
}),
);
| Option | Default | Description |
|---|---|---|
strict |
true |
Reject non-arrays/objects |
reviver |
null |
JSON.parse() reviver |
URL-encoded Options
app.use(
express.urlencoded({
extended: true, // Use qs library
limit: "100kb", // Max body size
parameterLimit: 1000, // Max parameters
depth: 5, // Max nested depth (qs)
}),
);
| Option | Default | Description |
|---|---|---|
extended |
true (v5) / false (v4) |
Parsing library |
parameterLimit |
1000 |
Max URL parameters |
depth |
32 |
Max nested depth (qs) |
Raw/Text Options
app.use(
express.raw({
limit: "5mb",
type: "application/octet-stream",
verify: (req, res, buf, encoding) => {
// Verify signature, etc.
},
}),
);
Common Patterns
Route-Specific Parsing
const jsonParser = express.json();
const urlencodedParser = express.urlencoded({ extended: true });
// Apply to specific routes
app.post("/api/json", jsonParser, (req, res) => {
res.json(req.body);
});
app.post("/api/form", urlencodedParser, (req, res) => {
res.send(`Hello ${req.body.name}`);
});
Recommended over global middleware for better control.
Custom Content-Type
// Parse custom MIME types as JSON
app.use(
express.json({
type: "application/vnd.api+json",
}),
);
// Multiple types
app.use(
express.json({
type: ["application/json", "application/csp-report"],
}),
);
// Type function
app.use(
express.json({
type: (req) => {
return req.get("Content-Type")?.includes("json");
},
}),
);
Verify Function
const crypto = require("crypto");
app.use(
express.json({
verify: (req, res, buf, encoding) => {
// Verify HMAC signature
const signature = req.get("X-Signature");
const hash = crypto
.createHmac("sha256", SECRET)
.update(buf)
.digest("hex");
if (signature !== hash) {
throw new Error("Invalid signature");
}
},
}),
);
Used for webhook signature validation (GitHub, Stripe, etc.).
Multiple Parsers
// Parse both JSON and URL-encoded
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Route receives first matching parser
app.post("/api/data", (req, res) => {
// req.body populated by JSON or urlencoded
res.json(req.body);
});
Custom Limit
// Different limits per route
const smallParser = express.json({ limit: "1kb" });
const largeParser = express.json({ limit: "50mb" });
app.post("/api/small", smallParser, handler);
app.post("/api/upload", largeParser, handler);
Migration Guide
Express 4.16.0+ (Built-in)
Before (Standalone)
const bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
After (Built-in)
// No require needed
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
Recommendation: Use Express built-in for new projects.
Express 4 vs Express 5
// Express 4 default
app.use(express.urlencoded({ extended: false }));
// Express 5 default (changed!)
app.use(express.urlencoded({ extended: true }));
Breaking change: extended default changed from false to true in Express 5.
Removing body-parser Dependency
# Remove from package.json
npm uninstall body-parser
// Update code
- const bodyParser = require('body-parser');
- app.use(bodyParser.json());
+ app.use(express.json());
Security
Validate User Input
app.post("/api/users", (req, res) => {
// CRITICAL: req.body is untrusted user input
const { name, email } = req.body;
// Validate before use
if (!name || typeof name !== "string") {
return res.status(400).json({ error: "Invalid name" });
}
if (!email || !email.includes("@")) {
return res.status(400).json({ error: "Invalid email" });
}
// Safe to use
saveUser({ name, email });
res.json({ success: true });
});
Never trust req.body without validation.
Set Reasonable Limits
// Prevent large payloads (DoS attacks)
app.use(express.json({ limit: "1mb" }));
app.use(
express.urlencoded({
extended: true,
limit: "1mb",
parameterLimit: 1000, // Prevent parameter pollution
}),
);
Verify Webhooks
const crypto = require("crypto");
function verifyGitHubSignature(req, res, buf) {
const signature = req.get("X-Hub-Signature-256");
const hash =
"sha256=" +
crypto.createHmac("sha256", WEBHOOK_SECRET).update(buf).digest("hex");
if (signature !== hash) {
throw new Error("Invalid signature");
}
}
app.use(
"/webhooks/github",
express.json({
verify: verifyGitHubSignature,
}),
);
Depth Limit (Nested Objects)
// Prevent deeply nested objects (DoS)
app.use(
express.urlencoded({
extended: true,
depth: 5, // Limit nesting to 5 levels
}),
);
Error Handling
Common Errors
| Error | Cause | Solution |
|---|---|---|
content encoding unsupported |
Compressed body with inflate: false |
Set inflate: true |
entity parse failed |
Invalid JSON/data format | Validate client input |
request entity too large |
Body exceeds limit |
Increase limit or reduce payload |
too many parameters |
Exceeds parameterLimit |
Increase limit or reduce params |
Unsupported charset "SHIFT_JIS" |
Non-UTF-8 encoding | Use UTF-8 or custom parser |
Error Handler
app.use(express.json());
app.use((err, req, res, next) => {
if (err instanceof SyntaxError && err.status === 400) {
// Handle JSON parse errors
return res.status(400).json({ error: "Invalid JSON" });
}
if (err.type === "entity.too.large") {
return res.status(413).json({ error: "Payload too large" });
}
next(err);
});
Handling Parse Failures
// Custom error handling for specific routes
const jsonParser = express.json();
app.post(
"/api/data",
(req, res, next) => {
jsonParser(req, res, (err) => {
if (err) {
return res.status(400).json({
error: "Failed to parse JSON",
details: err.message,
});
}
next();
});
},
handler,
);
Examples
JSON API Endpoint
app.use(express.json());
app.post("/api/users", async (req, res) => {
try {
const { name, email, age } = req.body;
// Validate
if (!name || !email) {
return res.status(400).json({
error: "Name and email required",
});
}
// Save to database
const user = await db.users.create({
name,
email,
age,
});
res.status(201).json(user);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
Form Submission
app.use(express.urlencoded({ extended: true }));
app.post("/contact", (req, res) => {
const { name, email, message } = req.body;
// Process form data
sendEmail({
to: "support@example.com",
from: email,
subject: `Message from ${name}`,
body: message,
});
res.redirect("/thank-you");
});
Webhook Handler
const crypto = require("crypto");
app.use(
"/webhooks/stripe",
express.json({
verify: (req, res, buf) => {
const signature = req.get("stripe-signature");
const hash = crypto
.createHmac("sha256", STRIPE_WEBHOOK_SECRET)
.update(buf)
.digest("hex");
// Stripe uses different signature format
req.rawBody = buf.toString();
},
}),
);
app.post("/webhooks/stripe", (req, res) => {
const event = req.body;
switch (event.type) {
case "payment_intent.succeeded":
handlePaymentSuccess(event.data.object);
break;
// ...
}
res.json({ received: true });
});
File Upload (Using Multer)
// body-parser does NOT handle multipart/form-data
const multer = require("multer");
const upload = multer({ dest: "uploads/" });
// JSON fields
app.use(express.json());
// File upload
app.post("/upload", upload.single("file"), (req, res) => {
// req.file contains file data
// req.body contains JSON fields
res.json({
file: req.file,
data: req.body,
});
});
Important: Use multer or similar for file uploads.
Conditional Parsing
// Only parse JSON for API routes
app.use("/api/*", express.json());
// Only parse forms for web routes
app.use("/web/*", express.urlencoded({ extended: true }));
// Specific routes get specific parsers
app.post("/api/users", (req, res) => {
// req.body is JSON
});
app.post("/web/contact", (req, res) => {
// req.body is URL-encoded form data
});
Gotchas
Does NOT Handle Multipart
// This will NOT work for file uploads
app.use(express.json());
app.post("/upload", (req, res) => {
console.log(req.body); // undefined!
});
Solution: Use multer, busboy, or formidable for multipart/form-data.
Extended Option Changed
// Express 4 default (querystring library)
express.urlencoded({ extended: false });
// Express 5 default (qs library)
express.urlencoded({ extended: true });
Issue: Migration to Express 5 changes parsing behavior.
Solution: Explicitly set extended option.
Order Matters
// WRONG: Parser after route
app.post("/api/data", (req, res) => {
console.log(req.body); // undefined!
});
app.use(express.json());
// RIGHT: Parser before routes
app.use(express.json());
app.post("/api/data", (req, res) => {
console.log(req.body); // Works!
});
Parsers must be registered before routes that use them.
Charset Support
// Only UTF-8 is supported by default
// This will fail:
// Content-Type: application/json; charset=SHIFT_JIS
Solution: Convert to UTF-8 before parsing or use custom parser.
Empty Body
app.post("/api/data", (req, res) => {
// req.body is {} if no body sent
console.log(req.body); // {}
console.log(Object.keys(req.body).length); // 0
});
Solution: Check Content-Length header or validate required fields.
Type Checking
// req.body is always an object (JSON/urlencoded)
// Even if client sends: 123
console.log(req.body); // { '123': '' } (urlencoded)
console.log(req.body); // 123 (JSON with strict: false)
Solution: Use strict: true for JSON parser to reject primitives.
Also see
- body-parser GitHub - Official repository
- Express API: express.json() - Express built-in docs
- Express API: express.urlencoded() - URL-encoded parser docs
- multer - Multipart/form-data parser for file uploads
- Express Error Handling - Error handling patterns
- qs library - Query string parser (extended: true)