Logonestjs-openapi
Guides

Decorators

Which NestJS and @nestjs/swagger decorators nestjs-openapi reads for routing, operations, parameters and security, and which it ignores.

The generator covers the most common NestJS + Swagger decorators. Schemas come from TypeScript types (not @ApiProperty); security schemes are defined in config and decorators add per-operation requirements.

Quick truth table: we read routing + HTTP method decorators, Swagger operation metadata (tags, operation, consumes/produces, ApiResponse), parameter descriptions from @ApiParam/@ApiQuery, and method-level exclusion decorators. We ignore property-level Swagger decorators and response shortcuts.

For a concise overview, see the Support Matrix.

Routing Decorators

Controller

@Controller('users')
export class UsersController {
  // All routes prefixed with /users
}

Controller versioning (@Controller({ path: 'users', version: '1' })) is not currently supported. We only read the string path.

HTTP Method Decorators

Standard HTTP method decorators are supported:

@Controller('users')
export class UsersController {
  @Get()
  findAll() {}

  @Get(':id')
  findOne(@Param('id') id: string) {}

  @Post()
  create(@Body() dto: CreateUserDto) {}

  @Put(':id')
  replace(@Param('id') id: string, @Body() dto: UpdateUserDto) {}

  @Patch(':id')
  update(@Param('id') id: string, @Body() dto: PatchUserDto) {}

  @Delete(':id')
  remove(@Param('id') id: string) {}
}

@Options() and @Head() are also supported. @All() is mapped to an all operation, which is not a standard OpenAPI method - prefer explicit verbs if you need strict validation.

Parameter Decorators

@Get(':id')
findOne(
  @Param('id') id: string,                    // Path parameter
  @Query('include') include?: string,         // Query parameter
  @Headers('authorization') auth: string,     // Header parameter
) {}

@Post()
create(
  @Body() dto: CreateUserDto,                 // Request body
) {}

Response Status

@Post()
@HttpCode(201)
create(@Body() dto: CreateUserDto) {}

@Delete(':id')
@HttpCode(204)
remove(@Param('id') id: string) {}

Swagger Decorators

@ApiTags

Assigns tags to controller endpoints:

@ApiTags('users')
@Controller('users')
export class UsersController {
  // All endpoints tagged with 'users'
}

// Multiple tags
@ApiTags('users', 'admin')
@Controller('admin/users')
export class AdminUsersController {}

@ApiOperation

Describes an operation:

@Get()
@ApiOperation({
  summary: 'List all users',
  description: 'Returns a paginated list of users',
  operationId: 'listUsers',
  deprecated: false,
})
findAll() {}
PropertyOpenAPI Field
summarysummary
descriptiondescription
operationIdoperationId
deprecateddeprecated

@ApiParam

Documents path parameters:

@Get(':id')
@ApiParam({
  name: 'id',
  description: 'User ID',
  type: String,
  required: true,
  example: '123e4567-e89b-12d3-a456-426614174000',
})
findOne(@Param('id') id: string) {}

@ApiQuery

Documents query parameters:

@Get()
@ApiQuery({
  name: 'page',
  description: 'Page number',
  type: Number,
  required: false,
  example: 1,
})
@ApiQuery({
  name: 'limit',
  description: 'Items per page',
  type: Number,
  required: false,
  example: 10,
})
findAll(
  @Query('page') page?: number,
  @Query('limit') limit?: number,
) {}

Query DTOs

When using @Query() without an explicit parameter name, DTO class properties are expanded into individual query parameters by default:

// DTO definition
class PaginationDto {
  page?: number;
  limit?: number;
}

// Controller
@Get()
findAll(@Query() pagination: PaginationDto) {}

// Generated OpenAPI parameters:
// - page (query, optional)
// - limit (query, optional)

This is a common OpenAPI style and works better with code generators. If you prefer to keep the DTO as a single schema reference, set query.style: "ref" in your config. See the Configuration Guide for details.

Explicitly named query params like @Query('filter') filter: FilterDto always use a schema reference, regardless of the query.style setting.

@ApiBody

Documents the request body:

@Post()
@ApiBody({
  description: 'User creation data',
  type: CreateUserDto,
  required: true,
})
create(@Body() dto: CreateUserDto) {}

@ApiResponse

Documents responses. We read status, description, type (including [UserDto] and () => UserDto), isArray and schema (including getSchemaPath()). As in @nestjs/swagger, a declared type or schema wins over the method's return type, and a response without status is the default response.

@Get(':id')
@ApiResponse({
  status: 200,
  description: 'User found',
  type: UserDto,
})
@ApiResponse({
  status: HttpStatus.NOT_FOUND,
  description: 'User not found',
})
findOne(@Param('id') id: string) {}

Response Shortcuts

Every status shortcut @nestjs/swagger exports is supported (@ApiOkResponse, @ApiCreatedResponse, @ApiNoContentResponse, @ApiBadRequestResponse, @ApiUnauthorizedResponse, @ApiNotFoundResponse, @ApiConflictResponse, @ApiDefaultResponse and the rest), with the same options as @ApiResponse.

Response decorators on the controller apply to every method. A method's own response for the same status wins:

@Controller('users')
@ApiUnauthorizedResponse({ description: 'Missing credentials' })
export class UsersController {
  @Get(':id')
  @ApiOkResponse({ type: UserDto })
  @ApiNotFoundResponse({ description: 'User not found' })
  findOne(@Param('id') id: string) {} // 200, 401 and 404
}

@ApiConsumes and @ApiProduces

Specify content types:

@Post('upload')
@ApiConsumes('multipart/form-data')
uploadFile(@Body() dto: FileUploadDto) {}

@Get('report')
@ApiProduces('application/pdf')
getReport() {}

@ApiExcludeEndpoint and @ApiExcludeController

Exclude endpoints, or every endpoint of a controller, from the generated spec:

@Get('health')
@ApiExcludeEndpoint()
healthCheck() {}

To exclude endpoints marked with your own decorators, list them in excludeDecorators.

Custom Decorators

Decorators you build with applyDecorators() are followed automatically, including the arguments they are called with:

export function FilterField(type = FilterDto) {
  return applyDecorators(
    ApiPropertyOptional({ type }),
    IsOptional(),
    ValidateNested(),
    Type(() => type),
  );
}

export const ApiStandardErrors = () =>
  applyDecorators(
    ApiBadRequestResponse({ type: ErrorDto }),
    ApiUnauthorizedResponse(),
  );

@FilterField() is read as @ApiPropertyOptional({ type: FilterDto }) plus the validation decorators, and @ApiStandardErrors() as its two responses. This works for property, method and controller decorators, for functions and constants, and for wrappers of wrappers.

Some decorators cannot be followed: those from a compiled library, or those that pick decorators with conditions. Describe them with the decorators option.

DTO Schema Generation

Schemas are generated from TypeScript types with ts-json-schema-generator, then @ApiProperty and class-validator metadata is applied on top. Metadata is read wherever the class is declared.

How Schemas Work

The library generates JSON Schema definitions by analyzing TypeScript source files:

// TypeScript types are converted to JSON Schema
export class CreateUserDto {
  email: string; // → { "type": "string" }
  age?: number; // → { "type": "number" } (optional)
  roles: string[]; // → { "type": "array", "items": { "type": "string" } }
}

Adding Descriptions with JSDoc

Use JSDoc comments to add descriptions to your schemas:

export class CreateUserDto {
  /** User email address */
  email: string;

  /**
   * User age in years
   * @minimum 0
   * @maximum 150
   */
  age?: number;
}

class-validator Integration

If you enable extractValidation: true, the library extracts constraints from class-validator decorators:

export class CreateUserDto {
  @IsEmail()
  email: string; // → { "type": "string", "format": "email" }

  @Min(0)
  @Max(150)
  age?: number; // → { "type": "number", "minimum": 0, "maximum": 150 }

  @MinLength(1)
  @MaxLength(100)
  name: string; // → { "type": "string", "minLength": 1, "maxLength": 100 }
}

See the Validation Guide for details.

Property Decorators

@ApiProperty and @ApiPropertyOptional add descriptions, examples, enums, formats, constraints and nullability. required: false and @ApiPropertyOptional() make a property optional even without ?. @ApiHideProperty() removes a property.

A property whose type is another class is a $ref to that class's schema. When the property also has a description or is nullable, it is wrapped as { allOf: [{ $ref }], description } in OpenAPI 3.0, like @nestjs/swagger does, and written as { $ref, description } in 3.1.

Mapped Types

Classes built with PartialType, PickType, OmitType and IntersectionType get the inherited properties with the helpers' rules, including nested helpers such as PartialType(OmitType(UserDto, ['id'])).

Type Inference

The library infers types from TypeScript when possible:

@Controller('users')
export class UsersController {
  // Return type inferred from method signature
  @Get()
  findAll(): Promise<UserDto[]> {
    return this.service.findAll();
  }

  // Parameter types inferred from decorators
  @Get(':id')
  findOne(@Param('id') id: string): Promise<UserDto> {
    return this.service.findOne(id);
  }
}

For best results, always specify explicit return types on controller methods and use typed DTOs for request bodies.

Security Decorators

Security decorators are fully supported at both controller and method level:

@Controller('users')
@ApiBearerAuth() // Applied to all methods
export class UsersController {
  @Get()
  findAll() {} // Inherits @ApiBearerAuth

  @Get(':id')
  @ApiOAuth2(['read:users']) // Method-level override
  findOne(@Param('id') id: string) {}

  @Delete(':id')
  @ApiBearerAuth('jwt')
  @ApiSecurity('admin-key') // Multiple schemes (AND logic)
  remove(@Param('id') id: string) {}
}

Supported Security Decorators

DecoratorDefault NameNotes
@ApiBearerAuth(name?)'bearer'HTTP Bearer (JWT)
@ApiBasicAuth(name?)'basic'HTTP Basic auth
@ApiOAuth2(scopes[], name?)'oauth2'OAuth2 with scopes
@ApiSecurity(name, scopes?)-Generic security scheme
@ApiCookieAuth(name?)'cookie'Cookie-based auth

Behavior

  • Controller-level: Applies to all methods in the controller
  • Method-level: Overrides controller-level security
  • Multiple decorators: AND logic (all schemes required)
  • Merge with config: Global security from config is merged into per-operation requirements; if both are present, all schemes are required

Security schemes must still be defined in config. Decorators reference scheme names defined in openapi.security.schemes.

See the Security Guide for complete configuration options.

Next Steps

On this page