Diagnosing Empty or Undefined req.body in Express
When Express route handlers see req.body as undefined or empty, the cause is usually middleware order, Content-Type mismatch, or payload limits. Learn ordered checks and fixes.
08 Jul 2025, 10:29 UTC

Recognizable Condition
A client sends a POST, PUT, or PATCH request with a JSON or URL-encoded payload, but the Express route handler sees req.body as undefined or an empty object ({}). Validation fails, or the data is silently dropped. The route itself runs, so the problem is almost always in how the request stream is parsed before it reaches the handler.
Useful takeaway: In most cases, the body parser is missing, registered after the route, or the request Content-Type does not match the parser you configured.
Cause and Diagnostic Table
| Symptom | Likely Cause | Quick Diagnostic |
|---|---|---|
req.body is undefined | No body parser registered, or parser registered after the route | Check that app.use(express.json()) appears before route definitions |
req.body is {} | Request Content-Type does not match the active parser, or the client sent no body | Inspect request headers and payload bytes |
HTTP 413 or entity.too.large | Payload exceeds the parser's default size limit | Compare Content-Length against the configured limit |
| HTTP 400 with parse error | Malformed JSON or URL-encoded data | Validate the payload syntax independently |
req.body is undefined on file upload | multipart/form-data sent to the JSON parser | Check Content-Type; built-in JSON parser ignores multipart |
Ordered Checks and Fixes
1. Compare the Request Content-Type Against the Active Parser
Express body parsers are Content-Type driven. express.json() only parses requests where the Content-Type header matches application/json. express.urlencoded() only parses application/x-www-form-urlencoded. If a client sends text/plain or omits the header, req.body remains undefined.
Fix: Align the client header with the parser. For a JSON API, send Content-Type: application/json. If you must accept multiple formats, register both parsers, but be aware that each parser checks the Content-Type and skips requests it does not recognize.
2. Confirm Middleware Registration Order
Express executes middleware in registration order. If you define a route before calling app.use(express.json()), the route handler runs before the parser, and req.body is never populated. This is one of the most common causes of an empty body.
Fix: Register body parsers before any route handlers. In a typical Express 4.16+ application, place app.use(express.json()) and app.use(express.urlencoded({ extended: true })) near the top of your middleware stack, after any logger or security middleware that does not consume the request stream.
3. Test with a Minimal Known-Good Payload
Rule out client-side encoding issues by sending a minimal, syntactically valid payload from the same machine running the server. This isolates the problem to the server configuration rather than the client library.
Run the following in your terminal (or command prompt on Windows) from the directory containing your project. No special permissions are required for a local request:
curl -H 'Content-Type: application/json' -d '{"x":1}' http://localhost:3000/your-route
Expected check: The route handler should log { x: 1 }. If req.body is still undefined, the parser is missing or misordered. If it works with curl but not with your application client, compare the headers your client sends.
4. Inspect Payload Size Against the Parser Limit
Both express.json() and express.urlencoded() default to a 100kb body limit. Larger payloads cause the parser to stop reading and emit an error, often surfacing as a 413 status or an unhandled entity.too.large event. If your error handler is not configured, the client may see a generic failure while req.body remains empty.
Fix: Raise the limit option when large payloads are expected, but pair the increase with validation or streaming for untrusted input to avoid memory pressure:
app.use(express.json({ limit: '1mb' }));
5. Check for Custom Middleware That Reads the Request Stream First
Node.js request streams are readable only once. If a custom middleware, proxy, or logging tool reads the raw stream before the body parser runs, the parser sees an empty or already-consumed stream and req.body stays undefined.
Fix: Ensure any middleware that reads the request body either runs after the parser or buffers and restores the stream. If you need the raw body for webhook signature verification, capture it inside a parser callback or use a dedicated raw-body parser before the JSON parser, then pass the buffered value forward.
Error Handling and Parser Selection
When a payload is malformed or exceeds the limit, the built-in parsers throw errors. Without an error-handling middleware, Express returns a generic 400 or 500, and the client receives no useful diagnostic. Add an error handler after your routes to catch parse failures:
app.use((err, req, res, next) => {
if (err.type === 'entity.too.large') {
return res.status(413).json({ error: 'Payload too large' });
}
if (err.type === 'entity.parse.failed') {
return res.status(400).json({ error: 'Invalid JSON' });
}
next(err);
});
Parser selection: Do not expect express.json() to parse multipart/form-data. It ignores that Content-Type. For file uploads or HTML form posts with binary data, use a multipart-aware library such as multer (or busboy directly). Registering multiple body parsers on the same route can also consume the stream; select the parser that matches the expected Content-Type.
Escalation Criteria
- Non-standard encodings: If you need to parse a custom or non-standard encoding, move to raw stream processing with
req.on('data')andreq.on('end'), or use a dedicated transformation middleware. - Proxy-dependent behavior: If
req.bodyis populated locally but empty behind a load balancer or reverse proxy, investigate whether the proxy is buffering the request, rewriting headers, or stripping theContent-Type. Check the proxy access logs for the inbound headers. - Version changes: If behavior changed after an upgrade, review the major-version release notes.
express.json()andexpress.urlencoded()are bundled starting in Express 4.16; earlier versions require the separatebody-parserpackage. Future major versions may change defaults or error semantics.
Limitations and Verification
Express 4 does not automatically catch rejected promises from asynchronous route handlers. If your handler is async and a body parser or validation step throws, the error may not reach your error-handling middleware unless you wrap the handler or use a wrapper so errors reach your error-handling middleware.
To verify your configuration after making changes, repeat the minimal curl test from Check 3. Confirm that the logged req.body matches the sent payload. Then test a malformed payload to ensure your error handler returns a 400 rather than an unhandled exception:
curl -H 'Content-Type: application/json' -d '{bad json}' http://localhost:3000/your-route
Finally, run npm ls express in your project directory to identify the installed major version, and compare the parser defaults against that version's documentation before changing limits or middleware.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.