# body-parser: the middleware that decides what req.body means

> Four parsers, a 100kb default limit, and an explicit warning that everything in req.body is untrusted. Here is how the module behaves, where its defaults are opinionated, and what it refuses to parse.

**expressjs/body-parser** — Node.js body parsing middleware

- Repository: https://github.com/expressjs/body-parser
- Stars: 5,500 · Forks: 793
- Language: JavaScript
- License: MIT
- Published: 2026-10-06 · Updated: 2026-10-06 · Language: en
- Canonical page: https://hysenlabs.com/projects/expressjs-body-parser

## Four parsers selected by Content-Type

The module does one job: it turns a request body into `req.body` before your handlers run. What makes it more than a JSON convenience is that it ships four separate parsers and picks between them by media type. The README lists them as the JSON body parser, the raw body parser, the text body parser and the URL-encoded form body parser. Each has its own factory function and its own options, and each only touches a request whose `Content-Type` header matches the `type` option.

That selection mechanism is worth understanding because it is where most integration surprises live. The `type` option can be a string, an array of strings, or a function. When it is not a function it is handed to the type-is library, which means it accepts an extension name like `json`, a media type like `application/json`, or a wildcard like `*/*` or `*/json`. When it is a function it is called as `fn(req)` and the request is parsed if the return value is truthy. The default for the JSON parser is `application/json`, which is why a request without a Content-Type header is not parsed at all.

```js
// Import all parsers
const bodyParser = require('body-parser')

// Or import individual parsers directly
const json = require('body-parser/json')
const urlencoded = require('body-parser/urlencoded')
const raw = require('body-parser/raw')
const text = require('body-parser/text')
```

The subpath imports in that snippet are real exports rather than a convention. The package manifest maps `./json`, `./raw`, `./text` and `./urlencoded` to files under `lib/types/`, alongside the main entry at `index.js` and the `package.json` file itself.

## Installing it and the Node version floor

Installation is the one command you would expect, and the module has no build step, no native dependency and no companion daemon:

```sh
$ npm install body-parser
```

The manifest sets `"type": "commonjs"` and declares `engines.node` as `>=18`, so this is CommonJS with a hard Node 18 floor. It is MIT licensed, and the repository credits Douglas Christopher Wilson and Jonathan Ong as contributors, with funding routed through Open Collective under the express organisation.

The runtime dependencies are all small and single-purpose: `bytes` for parsing human-readable size strings, `content-type` for parsing the incoming header, `debug` for conditional logging, `http-errors` for constructing the errors this module throws, `iconv-lite` for character set conversion, `on-finished` for knowing when a message has finished, `qs` for the extended query string parsing the URL-encoded parser uses, `raw-body` for reading the stream with limits and inflation, and `type-is` for the content type matching described above. Nothing here pulls in a framework, which is why the module works in plain Node HTTP servers as readily as in Express.

Development is Mocha with leak checking, coverage through nyc, and supertest for driving requests, plus eslint and neostandard for linting. The `files` array in the manifest ships only `lib/`, `LICENSE` and `index.js`, so the test suite and repository tooling stay out of your node_modules.

## The 100kb limit and why the README argues against raising it

The `limit` option controls the maximum request body size. A number is read as bytes, a string is passed to the bytes library for parsing, and the default is `'100kb'` across the parsers that support it. The README then does something unusual: it includes a block quoted recommendation not to configure a very high limit, and to use the default wherever possible.

The reasoning is given rather than asserted. Allowing larger payloads increases memory usage because of the resources required for decoding and transformations, and it can lead to longer response times as more data is processed. The README puts a number on what it considers very high: values above the default, for example payloads of 5 MB or more, can already introduce these risks. With the default limits, it says, these issues do not occur.

That is worth taking seriously in a specific context. A body parser sits between an untrusted network and your process, holds the request in memory to decode it, and does so on the event loop. A limit that is generous enough to be convenient for your largest legitimate request is also generous enough to be abused by anyone who finds the endpoint. The default is not a limitation to work around; it is the feature.

The `inflate` option is the companion control. Set to true, deflated bodies are inflated; set to false, they are rejected. It defaults to true, and all parsers also handle `gzip`, `br` for brotli, `deflate` and `zstd` encodings automatically. The README notes that `zstd` support requires Node.js `^22.15.0` or `>=23.8.0`, which is a tighter requirement than the module's overall Node 18 floor.

## Strict JSON by default, and the reviver and verify escape hatches

The JSON parser has three options that shape behaviour in ways worth knowing. `strict`, defaulting to true, accepts only arrays and objects; set it to false and it will accept anything `JSON.parse` accepts, which includes bare strings and numbers. For an API endpoint, strict is almost always what you want, because accepting a scalar body means the code downstream has to handle a shape you probably did not design for.

`defaultCharset` sets the character set assumed when the request's `Content-Type` header does not specify one. It defaults to `utf-8`. The parser accepts any Unicode encoding of the body, and the `iconv-lite` dependency is what makes that work.

`reviver` is passed directly to `JSON.parse` as its second argument, which means it is the standard JavaScript mechanism rather than something this module invented. The README links the MDN documentation for it.

`verify` is the more interesting one and the least used. If supplied it is called as `verify(req, res, buf, encoding)`, where `buf` is a Buffer of the raw request body, and throwing an error from it aborts the parsing. That is the hook for signature verification, checksum validation, or capturing the raw bytes before they are decoded, and it is the one option here that the module does not document with a worked example.

The raw parser exists for the cases where you want the Buffer rather than a decoded structure, which is what webhook signature checking and binary upload endpoints need. The text parser gives you a string. Both populate `req.body` the same way, so the type of that property depends entirely on which middleware you mounted.

## The untrusted-input warning and the multipart refusal

The README opens with a warning that deserves to be quoted rather than summarised, because it is the security documentation for the whole module. It notes that as `req.body`'s shape is based on user-controlled input, all properties and values in that object are untrusted and should be validated before being trusted. The example given is precise: `req.body.foo.toString()` may fail in multiple ways, because the `foo` property may not be there, may not be a string, and `toString` may not be a function but a string or other user input.

That is a TypeError waiting to happen, and in an Express application with an async error handler it becomes a 500 rather than a 400. The fix is validation at the boundary, which means a schema library rather than property access.

The second thing the module refuses is multipart. The README states plainly that this does not handle multipart bodies, due to their complex and typically large nature, and then does something more useful than declining: it lists four alternatives, busboy and connect-busboy, multiparty and connect-multiparty, formidable, and multer. That refusal is the right call. Multipart parsing is where streaming, disk spools and file size limits get complicated, and folding it into the same middleware as JSON would mean every request pays for the complexity.

Two further alternatives are named for the core job itself, the `body` module and `co-body`, so the README is candid that body-parser is not the only option for reading a request body in Node.

## Release cadence, the 1.x line, and what the manifest tells you

There is a version discrepancy worth noticing before you install anything. The most recent tagged releases are 1.20.6 on 2026-07-09, 1.20.7 on 2026-09-08, and 1.20.8 on 2026-09-08, and the last push was on 2026-09-08. But the package manifest on the default branch declares version 2.3.0. In other words the published 1.20.x line and the working manifest are different products, and the default branch is ahead of what npm serves.

That is not a criticism so much as a thing to plan around. If you want the stable published line, install from the registry and you get 1.20.8. If you are reading the repository to understand behaviour, you are reading code marked 2.3.0. The `HISTORY.md` file at the repository root is the changelog for the published line.

The three releases landing within about two months of each other, two of them on the same day, suggests a maintenance pattern of small patch releases rather than feature drops, which is the right shape for a dependency this widely used. The repository also carries an OpenSSF Scorecard badge and a test coverage badge, which are the sort of metadata that a project with this many downstream users tends to keep current.

For what it is worth on scope: this module is a parser and nothing more. It does not validate, does not coerce, does not sanitise, and does not enforce a schema. Everything in the file list at the repository root reflects that, with `index.js`, a `lib/` directory and a `test/` directory and very little else.

## Conclusion

body-parser is a small module with sharp opinions, and those opinions are the reason to read its options rather than copy a configuration. The 100kb limit, strict JSON by default, and the refusal to touch multipart bodies are decisions that protect a server from clients it does not control. The specific first step is to check whether the version you install matches what you expect: the manifest in the repository declares 2.3.0 while the most recent tagged release is 1.20.8, so the default branch and the published package are not the same thing. Then set `limit` deliberately rather than raising it, and read the README's note on untrusted input before your first handler.

## FAQ

### How do I install Body-parser using npm?

The README gives one command: npm install body-parser. There is no build step and no native dependency. The manifest declares CommonJS with Node 18 or later, and publishes subpath exports for each parser, so you can require the module itself or import a single parser such as body-parser/json.

### What is the default request body size limit in body-parser?

The limit option defaults to 100kb, and a string value is parsed by the bytes library. The README includes a recommendation against configuring a much higher limit, because larger payloads increase memory usage during decoding and lengthen response times, and it names payloads of 5 MB or more as already introducing those risks.

### Does body-parser handle file uploads and multipart form data?

No. The README states that it does not handle multipart bodies because of their complex and typically large nature, and points to busboy, multiparty, formidable and multer as the modules to use instead. For webhook signature checking, the raw parser gives you the undecoded Buffer.

### Why does body-parser throw a TypeError when I access req.body?

Because the shape of req.body comes from user-controlled input. The README warns that every property and value in it is untrusted and must be validated first, giving the example that req.body.foo.toString() can fail because foo may be absent, may not be a string, or may itself be the string toString. Validate the body at the boundary instead of assuming its shape.

## Sources

- [expressjs/body-parser on GitHub](https://github.com/expressjs/body-parser)
- [Issues](https://github.com/expressjs/body-parser/issues)
- [License: MIT](https://github.com/expressjs/body-parser/blob/master/LICENSE)
- [README](https://github.com/expressjs/body-parser/blob/master/README.md)
- [Releases](https://github.com/expressjs/body-parser/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/expressjs-body-parser
