-
Notifications
You must be signed in to change notification settings - Fork 0
Swagger
Describe the API surface for @Crud()-generated routes. @nestjs-crud/core auto-generates operation summaries, per-route markdown descriptions, error responses, request-body examples, and query-parameter documentation. Consumers can override or opt out via @Crud({ swagger: {...} }).
@nestjs/swaggeris an optional peer dependency. When it is not installed,@nestjs-crud/coresilently skips all Swagger decoration viasafeRequire. See Swagger-less mode below.
npm i @nestjs/swaggerimport { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('My API')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('/api', app, document);
await app.listen(3000);
}
bootstrap();UI: http://localhost:3000/api. JSON: http://localhost:3000/api-json. For deeper SwaggerModule options (custom UI, auth, multi-document setups), see the upstream NestJS OpenAPI docs.
import { ApiProperty } from '@nestjs/swagger';
export class User {
@ApiProperty() id: number;
@ApiProperty() name: string;
@ApiProperty({ required: false }) email?: string;
}import { Controller } from '@nestjs/common';
import { Crud, CrudController } from '@nestjs-crud/core';
import { User } from './user.entity';
import { UsersService } from './users.service';
@Crud({ model: { type: User } })
@Controller('users')
export class UsersController implements CrudController<User> {
constructor(public service: UsersService) {}
}That produces eight routes with imperative summaries (List users, Get user by id, Create users in bulk, ...), per-route markdown descriptions, 400 and 404 error responses, and request-body examples synthesized from @ApiProperty metadata.
-
Auto
@ApiTags. Controllers without an explicit@ApiTagsget one assigned from the pluralized entity name (User→Users). If the class is already decorated with@ApiTags(...), the existing tag wins and no auto-assignment happens. -
Imperative operation summaries.
List users,Get user by id,Create user,Create users in bulk,Partially update user,Replace user,Delete user,Restore soft-deleted user. -
Per-route markdown descriptions referencing supported query parameters, validation groups (
CrudValidationGroups.CREATEandUPDATE), and soft-delete semantics where relevant. -
Error responses.
400 Bad Requeston every generated route.404 Not Foundon single-resource routes (get,update,replace,delete,recover).401 Unauthorizedwhen the controller is decorated with@CrudAuth(). -
Request-body examples. Create, update, and replace routes ship an example payload synthesized from the entity's
@ApiPropertymetadata. -
Query-parameter documentation with
Docsbacklinks to the Query Syntax wiki page, covering?s=,?filter=,?or=,?sort=,?fields=,?join=,?limit=,?offset=,?page=,?cache=. -
Outcome-focused response text (
Paginated list of matching resources,Resource created,Resource removed).
All fields below are optional. Override only what you need.
| Option | Type | Default | Purpose |
|---|---|---|---|
tag |
string | string[] |
pluralized entity name | Override the auto-assigned @ApiTags value. |
tagWithVersion |
boolean |
false |
On versioned controllers (@Controller({ version })), prepend v{version}/ to the default tag so auto-tags do not collide across API versions. Has no effect when tag is set manually. |
description |
string |
undefined |
Free-form controller-level description surfaced in the emitted OpenAPI metadata. Consumer prose; do not interpolate untrusted input. |
examples |
boolean |
true |
Set to false to opt out of request-body example synthesis on create, update, and replace routes. |
synthExample |
(entity: any, route: BaseRouteName) => unknown |
built-in @ApiProperty synthesizer |
Supply your own example synthesizer. Takes precedence over the built-in path. The return value ships verbatim into the emitted OpenAPI JSON, so do not return secrets. |
operations |
Partial<Record<BaseRouteName, Omit<Partial<ApiOperationOptions>, 'operationId'>>> |
{} |
Per-route overrides for generated operation metadata (summary, description, tags, responses). operationId is intentionally omitted (see callout below). |
errorResponses.unauthorized |
boolean |
auto-emitted only when @CrudAuth() is present |
Force-emit 401 Unauthorized on every generated route even without @CrudAuth(). Useful when authentication is enforced via a globally-registered guard (APP_GUARD). |
@Crud({
model: { type: User },
swagger: {
tag: 'Users',
description: 'User account management — all operations require a session token.',
},
})
@Controller('users')
export class UsersController implements CrudController<User> {
constructor(public service: UsersService) {}
}@Crud({
model: { type: User },
swagger: { examples: false },
})
@Controller('users')
export class UsersController implements CrudController<User> {
constructor(public service: UsersService) {}
}import { BaseRouteName } from '@nestjs-crud/core';
@Crud({
model: { type: User },
swagger: {
synthExample: (entity: any, route: BaseRouteName) => {
if (route === 'createOneBase') {
return { name: 'Ada Lovelace', email: 'ada@example.com' };
}
return {};
},
},
})
@Controller('users')
export class UsersController implements CrudController<User> {
constructor(public service: UsersService) {}
}@Crud({
model: { type: User },
swagger: {
operations: {
getManyBase: {
summary: 'Paginated user list',
description: 'Returns up to 100 users. Use `?page=N` for pagination.',
},
},
},
})
@Controller('users')
export class UsersController implements CrudController<User> {
constructor(public service: UsersService) {}
}
operationIdcannot be overridden. It is rejected at compile time (the type signature usesOmit<..., 'operationId'>) and re-applied at runtime using the canonical{routeName}{ControllerName}{ModelName}format. OpenAPI requiresoperationIduniqueness across the full emitted document, and consumer overrides would reintroduce duplicate-id footguns.
Use this when your app enforces authentication via an APP_GUARD provider rather than the @CrudAuth() decorator. Without it, the 401 path is absent from the emitted OpenAPI document.
@Crud({
model: { type: User },
swagger: { errorResponses: { unauthorized: true } },
})
@Controller('users')
export class UsersController implements CrudController<User> {
constructor(public service: UsersService) {}
}When app.enableVersioning() is active and multiple controllers share a model name across versions, the default auto-tag can collide (for example Users for both a v1 and a v2 controller). Setting swagger.tagWithVersion: true prepends v{version}/ read from NestJS's controller-version metadata:
@Crud({ model: { type: User }, swagger: { tagWithVersion: true } })
@Controller({ version: '2', path: 'users' })
export class UsersControllerV2 implements CrudController<User> {
constructor(public service: UsersService) {}
}Emitted tag: v2/Users (disambiguated from the v1 Users tag). Has no effect when tag is set manually, or when the controller has no version metadata.
Every built-in query parameter (?s=, ?filter=, ?or=, ?sort=, ?fields=, ?join=, ?limit=, ?offset=, ?page=, ?cache=) ships with a description carrying a Docs backlink to the Query Syntax wiki page. Consumers do not configure this; it is always on.
@nestjs/swagger is declared as an optional peer dependency. When it is not installed, @nestjs-crud/core uses safeRequire to silently skip every Swagger decoration path. @Crud() controllers still work; only the OpenAPI document is unavailable.
A dedicated CI sentinel job (test (no-swagger) in .github/workflows/tests.yml) runs the full test suite without @nestjs/swagger installed, verifying that this path stays functional on every push.
Internal API change in v2.0.0.
Swagger.operationsMap(modelName)now returns{ summary, description }tuples per route instead of plain summary strings.
Consumers who subclassed CrudRoutesFactory on v1.x and imported Swagger.operationsMap directly must destructure the new shape:
// v1.x:
const summary = Swagger.operationsMap(this.modelName)[name];
Swagger.setOperation({ summary, ... }, this.targetProto[name]);
// v2.0.0+:
const { summary, description } = Swagger.operationsMap(this.modelName)[name];
Swagger.setOperation({ summary, description, ... }, this.targetProto[name]);The new per-route markdown description (referencing supported query parameters and validation groups) ships alongside the summary; destructure and forward it into setOperation to preserve the full generated metadata.
-
Controllers —
@Crud()decorator, request lifecycle, route generation -
Query Syntax — target of the auto-emitted
Docsbacklinks - ServiceTypeorm, ServicePrisma, ServiceDrizzle, ServiceMikroOrm — per-adapter setup
-
NestJS OpenAPI docs — upstream
SwaggerModulesetup, custom UI, auth integration