Koa Router-wrapper adding type-safety, schema validation and openapi generation.
npm install koa-okapi-router> Zod-aware router abstraction for Koa, providing a declarative Fastify-style
> schema declaration and type-safe middleware input/output




- Declare routes with Zod schemas
- Type-safe ctx: ParameterizedContext in route handling middleware
- Support for zod v3 and v4
- Generate openapi.json
- Can co-exist with vanilla KoaRouter and e.g, swagger-jsdoc in the same application
Create a router without asking any questions or taking down any names.
``typescriptONECore ${config.applicationName}
const router = makeOkapiRouter(new KoaRouter(), {
openapi: {
info: { title: },`
},
})
Create a router with openapi metadata
`typescriptExotic Animal Spotting API
const router = makeOkapiRouter(new KoaRouter(), {
openapi: {
info: {
title: `
version: '1.2.3'
},
},
})
Create a router by bringing your own zod instance. Useful when you rely on importing zod/v3 or zod/v4 and cannotzod
use the default import.
`typescript`
const router = makeOkapiRouter(new KoaRouter(), {
schema: {
zod: z
},
})
`typescript`
router.get(
'/contacts/by-phone-number/:phoneNumber',
{
summary: 'Get a single contact by phone number',
description: 'A somewhat more long-winded description',
tags: ['Contacts'],
params: {
phoneNumber: {
description: 'Phone number to search for',
schema: z.string(),
},
response: {
200: z.object({
content: ContactSchema,
meta: z.object({}),
}),
404: z.null(),
},
},
async (ctx) => {
// ...
}
)
app.use(router.routes())
`typescript
router.addEntity('Contact', ContactSchema)
router.get(
'/contacts/by-phone-number/:phoneNumber',
{
...
response: {
200: {
// Providing a canonical name for the response body registers it as a component entity
name: 'ContactResponse',
schema: z.object({
content: ContactSchema,
meta: z.object({})
})
}
...
}
...
)
`
`typescript
app.use(
new KoaRouter()
.get(okapiRouter.openapiJsonUrl, async (ctx) => {
ctx.body = api.openapiJson()
})
.routes()
)
app.use(
koaSwagger({
routePrefix: '/swagger',
swaggerOptions: { url: api.openapiJsonUrl },
})
)
`
Adopting koa-okapi-router doesn't have to be all in, in terms of producing a single openapi.json either.
Existing projects looking to migrate can do so gradually by generating the json schema with
the current tools as long as you have access to the schema model object.
okapiRouter.extendOpenApiJson takes your existing schema as input, and generates the routes
and components from an okapiRouter into that schema.
`typescript
// OpenAPI schema generated by swaggerJsDoc (or by other means)
const swaggerJsDocSchema = swaggerJsdoc(swaggerSpec) as any
// Combined schema to serve and in place of swaggerJsDocSchema
const openApiJson = okapiRouter.extendOpenApiJson(swaggerJsDocSchema)
``
See CHANGELOG.md for version history and unreleased changes.
© 2025 Sven Johansson. ISC Licensed