POST, PUT and PATCH endpoints are useless until you can read what the client sent.
Express doesn't parse request bodies by default — the body is still a raw stream. Add the built-in body-parsing middleware:
app.use(express.json())— forContent-Type: application/json. Parsed data appears onreq.body.app.use(express.urlencoded())— for HTML form posts (application/x-www-form-urlencoded).express.text()andexpress.raw()for plain text and binary (rawgives a Buffer — useful for verifying webhook signatures).
Each parser only acts when the request's Content-Type matches its type option. In Express 5, if no parser ran, `req.body` is `undefined` (Express 4 gave {}), so code like req.body.name throws on a request sent without a JSON content type. Destructure defensively or validate first.
Options worth knowing:
limit— maximum body size, default"100kb". Larger bodies get a 413 error. Raise it only where needed, ideally on a specific route:app.post("/import", express.json({ limit: "5mb" }), handler).strict(json, defaulttrue) — only accept objects and arrays at the top level.extended(urlencoded) — Express 5 defaults tofalse, using the simplequerystringparser; settruefor nesteda[b]=csyntax via theqslibrary.
Malformed JSON produces an error with status 400 and type: "entity.parse.failed"; it flows to your error handler, so respond with a helpful 400 there instead of a stack trace.
req.body is untrusted: it can be any JSON shape, including a number, extra fields, or __proto__ keys. Validate it (a later lesson) before using it. File uploads (multipart/form-data) need a different parser such as multer.
Example
import express from "express";
const app = express();
app.use(express.json()); // application/json -> req.body
app.use(express.urlencoded()); // HTML forms (extended: false in v5)
app.post("/messages", (req, res) => {
const text = req.body?.text; // body is undefined if not parsed
if (typeof text !== "string" || text.length === 0) {
return res.status(400).json({ error: "text is required" });
}
res.status(201).json({ text, length: text.length });
});
app.post("/import", express.json({ limit: "5mb" }), (req, res) => {
res.json({ items: Array.isArray(req.body) ? req.body.length : 0 });
});
app.use((err, req, res, next) => { // malformed JSON, too large, ...
if (err.type === "entity.parse.failed") return res.status(400).json({ error: "invalid JSON" });
if (err.type === "entity.too.large") return res.status(413).json({ error: "body too large" });
next(err);
});
app.listen(3000);Remember
- Add
express.json()/express.urlencoded(); nothing is parsed by default - Express 5:
req.bodyisundefinedwhen no parser ran - Default limit is 100kb; raise it per route when needed
- Malformed JSON becomes a 400-status error for your error handler
Watch out for
- Testing with curl but forgetting
-H 'Content-Type: application/json', soreq.bodyis undefined.