Skip to content
This repository was archived by the owner on Jul 17, 2026. It is now read-only.

Body parsing

Eugene Lazutkin edited this page Apr 19, 2026 · 1 revision

Body parsing

The adapter handles request bodies in two modes, picked automatically per request.

Decision flow

Is ctx.request.body defined?
├─ Yes → use it as-is (body-parser middleware already handled parsing)
└─ No  → stream ctx.req, enforce maxBodyBytes, JSON.parse the result

Pre-parsed body (recommended)

Most Koa apps already have koa-bodyparser or @koa/bodyparser in the middleware chain for other routes. When ctx.request.body !== undefined, the adapter uses that value directly.

import bodyParser from 'koa-bodyparser';

app.use(bodyParser({jsonLimit: '2mb'}));
app.use(mount('/planets', createKoaAdapter(planets)));

In this mode the body parser's own size cap applies; the adapter's maxBodyBytes is not consulted. This keeps the body-size policy in one place (the middleware chain), which is usually what you want.

Advantages:

  • One place to configure JSON parsing (including non-JSON content types if you need them).
  • Other middleware downstream / upstream can inspect / transform the body.
  • Consistent ctx.request.body shape across all routes.

Stream-parsed body (fallback)

If no body parser is installed, the adapter consumes ctx.req (the raw Node IncomingMessage) directly, enforces a byte cap, and JSON.parses the result. Rejected on cap overflow or malformed JSON.

app.use(createKoaAdapter(planets, {maxBodyBytes: 256 * 1024}));

Useful when:

  • Your app is small enough that a body parser isn't worth pulling in.
  • You want per-adapter body-size policy (e.g. a bulk-load endpoint accepts 16 MiB while everything else caps at 64 KiB).
  • You want to stay zero-deps beyond what's strictly required.

Default cap: 1048576 (1 MiB).

Error responses

Condition Status code
Body exceeds maxBodyBytes 413 PayloadTooLarge
Body is not valid JSON 400 BadJsonBody
Body is missing required shape 400 BadLoadBody

BadLoadBody specifically fires on PUT /-load when the body isn't an array.

Mixing modes in one app

You can run multiple adapters with different body-cap policies:

app.use(bodyParser({jsonLimit: '256kb'}));                    // default for most routes
app.use(mount('/planets', createKoaAdapter(planets)));        // uses pre-parsed body (256 KiB cap)
app.use(mount('/bulk',    createKoaAdapter(bulk, {           // but pre-parsed means the bodyparser cap applies —
  maxBodyBytes: 16 * 1024 * 1024                             // `maxBodyBytes` here is ignored as long as
})));                                                         // bodyParser ran first.

If you need per-route body-size policy, mount the bulk adapter before the global body parser, or scope the body parser to specific routes using koa-mount / a router.

// Bulk route streams its own body (no bodyparser upstream → maxBodyBytes kicks in).
app.use(mount('/bulk', createKoaAdapter(bulk, {maxBodyBytes: 16 * 1024 * 1024})));
// Everything else uses the shared bodyparser.
app.use(bodyParser({jsonLimit: '256kb'}));
app.use(mount('/planets', createKoaAdapter(planets)));

Why we don't call req.destroy() on overflow

Some stream-parsers destroy the underlying socket when they see an over-cap body, to prevent the client from uploading more. This adapter does not — Koa still needs the socket alive to write the 413 response. Backpressure plus the aborted flag stop the reader from buffering; the response completes normally; the connection closes via the framework's standard response-end path.

The worst case is a few kernel-buffer bytes per over-sized request — negligible compared to letting the framework respond.

Reading the raw stream ourselves

The stream reader is a small utility exposed under dynamodb-toolkit-koa/read-body.js:

import {readJsonBody} from 'dynamodb-toolkit-koa/read-body.js';

// Elsewhere in your Koa app:
const body = await readJsonBody(ctx.req, 1024 * 1024);

Normally you won't need this — the adapter handles it internally. It's exported so consumers can reuse the same cap-enforced JSON reader in unrelated routes or when writing their own middleware for edge cases.

Clone this wiki locally