This package is designed for legacy or incremental migrations where documentation should stay as metadata on controller methods instead of becoming application middleware. It uses @asteasolutions/zod-to-openapi under the hood and keeps the authoring experience centered on a single @openapi(...) decorator.
- Zod v4+ only
@openapi(...)method decorator- Request body shorthand for the common JSON case
- OpenAPI 3.0 and 3.1 document generation
- Re-exports
zwith.openapi(...)already enabled
bun add @devscast/zod-openapi zodIf your project uses legacy decorators, enable them in tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true
}
}import{generateOpenApiDocument,openapi,z}from"@devscast/zod-openapi";constUserParamsSchema=z.object({user_id: z.string().min(1),});constPermissionsSchema=z.object({permissions: z.array(z.string()),}).openapi("Permissions");classUsersController{
@openapi({method: "put",path: "/api/users/:user_id/permissions",tags: ["Users"],summary: "Update User Permissions",description: "Update permissions for a specific user by their ID.",request: {params: UserParamsSchema,body: PermissionsSchema,},responses: {200: {description: "Updated permissions",content: {"application/json": {schema: z.object({id: UserParamsSchema.shape.user_id,}),},},},},})updatePermissions(){returnnull;}}constdocument=generateOpenApiDocument({controllers: [UsersController],document: {openapi: "3.0.0",info: {title: "Example API",version: "1.0.0",},},});document.paths will contain /api/users/{user_id}/permissions even though the decorator used the Express-style :user_id path.
If you want to register extra components or mix manual routes with decorated ones, build a registry explicitly:
import{OpenApiGeneratorV3,createOpenApiRegistry,}from"@devscast/zod-openapi";constregistry=createOpenApiRegistry({controllers: [UsersController],routes: [{method: "get",path: "/health",tags: ["System"],summary: "Health check",responses: {200: {description: "OK",},},},],register(registry){registry.registerComponent("securitySchemes","bearerAuth",{type: "http",scheme: "bearer",bearerFormat: "JWT",});},});constdocument=newOpenApiGeneratorV3(registry.definitions).generateDocument({openapi: "3.0.0",info: {title: "Example API",version: "1.0.0",},});Use generateOpenApi31Document(...) when you want a 3.1 document:
import{generateOpenApi31Document}from"@devscast/zod-openapi";constdocument=generateOpenApi31Document({controllers: [UsersController],document: {openapi: "3.1.0",info: {title: "Example API",version: "1.0.0",},},});