Swagger
@miiajs/swagger scans your controllers for decorator metadata and generates an OpenAPI 3.1 spec with Swagger UI.
Installation
npm install @miiajs/swagger swagger-ui-dist
Setup
import { SwaggerModule } from '@miiajs/swagger'
const app = new Miia()
.use(cors())
.register(AppModule)
SwaggerModule.setup(app, {
title: 'My API',
version: '1.0.0',
description: 'API documentation',
servers: [
{ url: 'http://localhost:3000', description: 'Development' },
],
securitySchemes: {
bearer: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
},
})
await app.listen(3000)
Endpoints:
GET /docs— Swagger UIGET /docs/json— OpenAPI JSON spec
Setup options
interface SwaggerSetupOptions {
title: string // Required
version: string // Required
description?: string
servers?: Array<{ url: string; description?: string }>
securitySchemes?: Record<string, any>
globalSecurity?: Array<Record<string, string[]>>
path?: string // Spec path (default: '/docs/json')
uiPath?: string // UI path (default: '/docs')
ui?: boolean // Serve UI (default: true)
swaggerOptions?: Record<string, any>
}
Decorators
@ApiTag
Group routes under tags (class-level):
@ApiTag('Users')
@Controller('/users')
class UserController {}
@ApiOperation
Describe an endpoint (method-level):
@ApiOperation({
summary: 'Create a new user',
description: 'Creates a user and returns the created object',
operationId: 'createUser',
deprecated: false,
})
@Post('/')
create(ctx: RequestContext) {}
@ApiResponse
Define response schemas (method-level, stackable):
@ApiResponse(200, {
description: 'User found',
schema: UserResponseSchema, // Zod schema or JSON Schema
})
@ApiResponse(404, { description: 'User not found' })
@Get('/:id')
findOne(ctx: RequestContext) {}
@ApiParam
Document path parameters (method-level, stackable):
@ApiParam('id', {
description: 'User ID',
schema: { type: 'string', format: 'uuid' },
})
@Get('/:id')
findOne(ctx: RequestContext) {}
Path parameters from :paramName patterns are auto-detected.
@ApiQuery
Document query parameters (method-level, stackable):
@ApiQuery('limit', { description: 'Max results', required: false })
@ApiQuery('offset', { description: 'Pagination offset', required: false })
@Get('/')
list(ctx: RequestContext) {}
Query parameters from @ValidateQuery schemas are auto-detected.
@ApiHeader
Document request headers (class or method-level, stackable):
@ApiHeader('X-Api-Key', { description: 'API key', required: true })
@Controller('/admin')
class AdminController {}
@ApiSecurity
Declare security requirements (class or method-level):
@ApiSecurity('bearer')
@Controller('/api')
class ApiController {}
// With scopes
@ApiSecurity('oauth2', ['write:users'])
@Delete('/:id')
remove(ctx: RequestContext) {}
@ApiExclude
Hide routes or controllers from the spec:
@ApiExclude()
@Controller('/internal')
class InternalController {}
// Or exclude a single route
@ApiExclude()
@Get('/debug')
debug() {}
Auto-detection
The spec builder automatically detects:
- Path parameters from route patterns (
:id->{id}) - Query parameters from
@ValidateQueryschemas - Request body from
@ValidateBodyschemas - Default responses (200/201 based on method)
- 422 response when validation decorators are present
- 403 response when guards are present
Schema support
Decorators accept any ZodLike schema (Zod v3, v4, or custom) and raw JSON Schema objects:
import { z } from 'zod'
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(100),
email: z.string().email(),
role: z.enum(['user', 'admin']),
age: z.number().int().optional(),
})
@ApiResponse(200, { schema: UserSchema })
Zod types are converted to JSON Schema automatically.
Complete example
@ApiTag('Users')
@ApiSecurity('bearer')
@Controller('/users')
@UseGuard(AuthGuard())
class UserController {
@Get('/')
@Public()
@ApiOperation({ summary: 'List all users' })
@ApiResponse(200, { schema: z.array(UserSchema) })
list() {}
@Get('/:id')
@ApiOperation({ summary: 'Get user by ID' })
@ApiParam('id', { description: 'User ObjectId' })
@ApiResponse(200, { schema: UserSchema })
@ApiResponse(404, { description: 'Not found' })
findOne(ctx: RequestContext) {}
@Post('/')
@Status(201)
@ValidateBody(CreateUserSchema)
@ApiOperation({ summary: 'Create user' })
@ApiResponse(201, { schema: UserSchema })
create(ctx: RequestContext) {}
@Delete('/:id')
@ApiOperation({ summary: 'Delete user' })
@ApiParam('id')
@ApiResponse(204, { description: 'Deleted' })
remove(ctx: RequestContext) {}
}
Exports
import {
SwaggerModule,
ApiTag,
ApiOperation,
ApiResponse,
ApiParam,
ApiQuery,
ApiSecurity,
ApiHeader,
ApiExclude,
SpecBuilder,
convertSchema,
} from '@miiajs/swagger'