Express
Mount a Kizuna API on an Express 5 application.
@kizunajs/express serves a Kizuna API from an Express 5 application. It handles routing, request validation, body parsing, and error formatting, all driven by what you declared.
Requires Express ≥ 5.
The adapter
expressAdapter() is what you name in kizuna.config.ts. Naming it is what gives every handler on this API Express's own req and res, typed.
Handler context
Each handler receives { params, query, body, headers, req, res }. The req and res are the native Express objects, useful when you need to read cookies, set custom headers, or stream a response.
Guards
A guard sits on the identity it authenticates, and receives the credential extracted and typed, alongside the native req/res. See the Authentication guide.
Middleware
For request-scoped values handlers need, use request context. Rate limiting, multipart parsing and the rest go in Express's own app.use. Authentication belongs in guards.
Options
| Option | Default | Description |
|---|---|---|
responseValidation | false | Validate handler return values against response schemas, surfacing as 500 on mismatch. Enable in development. |
json | Express's defaults | The options for express.json(), like limit and strict, used for Kizuna's own routes. Leave it out if the app runs its own express.json(). |
formatError | none | Reshape error (>= 400) response bytes for migrating clients. Most don't need it (use Problem Details extension members). See Migrating an existing API. |
Type-safe request properties
Middleware that sets custom properties on req, such as a request id, needs declaration merging to extend the Express Request interface. Authentication data does not: guards hand handlers typed context directly.
Both middleware and handlers are then typed:
Method mismatches
If a path is matched but the method is not declared, the adapter returns 405 with an Allow header listing the supported methods. This follows RFC 9110 §15.5.6.
Alongside your own Express routes
An app with Express routes of its own usually runs express.json() for them. Kizuna works with that: its routes use the body express.json() already parsed. Set it up one of two ways.
Mount Kizuna first
Kizuna's routes answer before express.json() runs, so it only parses for yours:
Or keep the raw body
With express.json() registered first, pass keepRawBody as its verify option, so routes with rawBody: true still get the body as it was sent:
Without either, a rawBody route answers 500, and the log names the route and both fixes.
Multipart / file uploads
For routes with contentType: 'multipart/form-data', add a multipart middleware such as multer or busboy before the Kizuna handler. The adapter reads req.body as-is.
Streams
A stream response is piped to res after flushHeaders(), so the client sees the headers before the first message. The generator's signal fires when res closes before the stream has finished.