NexusCS

body-parser

Node.js
Node.js body parsing middleware for Express. Parses incoming request bodies (JSON, URL-encoded, raw, text) before your handlers. Built into Express 4.16.0+.
express
middleware
http
json

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