-
Notifications
You must be signed in to change notification settings - Fork 2
In‐code swagger descriptions
Swagger description for API endpoints is generated from API code. The generation is provided by Nest's packages, which docs can be found here. Generated swagger can be found in browser from the {api-url}/swagger and it will be auto-updated when changes are made to the code.
Developer that changes an endpoint or adding a new one is responsible for writing the swagger description for it. When you are writing the description keep in mind that the main reader of it is client side developer from other team. This means that you should avoid using to technical descriptions if not necessary or refer to some internals of the API. If it is necessary to describe a technical implementation, it is good to add some links to external resources if any exists, although not necessary.
When you are changing or adding a new endpoint, you should also mark this endpoint as changed with a @SwaggerTags() decorator, where should be two tags: release tag (will change every sprint) and the tag of the controller. It must be under the JSDocs, best place is under the @ApiResponseDescription() decorator.
For example a /clan GET endpoint was changed:
@SwaggerTags('Release on 01.06.2025', 'Clan')
public getAll(@GetAllQuery() query: IGetAllQuery) {There are several things that need to be done for almost every endpoint in order to have a proper API documentation:
- Endpoint description
- DTO fields descriptions and examples
- API response
- Swagger tag
Description for an endpoint can be added by writing JSDocs comment for a controller class method handling the request. The description consists of two parts: short description and full description.
Short description is placed in the top of the comment. As the name suggests it should be short ideally 3-5 words describing what the endpoint does.
Full description can be of any length and describe what endpoint does and how the endpoint works and/or can be used by the client.
Here is the example of the the comment:
/**
* Get top clans
*
* @remarks Leaderboard of clans. Top Clans are defined by the amount of points that each Clan has.
*
* Notice that the leaderboards data is updated once every 3h hours.
*/
...
@Get('clan')
...
async getClanLeaderboard() {
...The field description should be pretty short 2-4 words. There should also be an example of the field. If the field is an unique, the information of it can be added only via @ApiProperty() decorator.
The validation of a field is picked up automatically and added to the swagger, so there is no need to write about it.
Example:
export class CreateClanDto {
/**
* Name of the clan.
* @example "my_clan"
*/
@ApiProperty({
uniqueItems: true,
})
@IsString()
@MaxLength(20)
name: string;
/**
* Short tag used to identify the clan.
* @example "CLN123"
*/
@IsString()
tag: string;
...API response can be described via a custom decorator @ApiResponseDescription(), which combines multiple Nest decorators and reduces some boiler plate code. It consists of two pieces: success response and possible error responses.
By default all success response codes are set 200, if status field is not specified.
It also adds authentication requirement for an endpoint, so if there are no authentication required (@NoAuth() is used), you should specify it separately with hasAuth: false option.
A special error case, when something unexpected happen - 500 error - is added to each endpoint separately and can not be removed.
If endpoint is returning an array of items, you need to specify returnsArray: true and also if the endpoint does not have a pagination the hasPagination: false should be added.
If there are some special case, when you need to add some other than usual description, you can use the Nest's decorators.
Examples:
One item returned:
@ApiResponseDescription({
success: {
dto: ClanDto,
status: 201,
modelName: ModelName.CLAN,
},
errors: [400, 401, 403, 409],
})
@Post()
...
public async create() {Multiple items returned:
@ApiResponseDescription({
success: {
dto: PlayerDto,
modelName: ModelName.PLAYER,
},
errors: [401, 404],
})
@Get()
...
public async getAll() {One item returned with no authentication:
@ApiResponseDescription({
success: {
dto: ClanDto,
modelName: ModelName.CLAN,
returnsArray: true,
},
errors: [400, 404],
hasAuth: false,
})
...
async getClanLeaderboard() {Nothing is returned, 204 response:
@ApiResponseDescription({
success: {
status: 204,
},
errors: [400, 401, 403, 404, 409],
})
@Put()
...
public async update() {If you are adding a new module with controller, it is most likely you need to add a tag for it with description. The description should not be too long and a sentence is usually ok. You need to add a tag in src/common/swagger/tags folder to tags.ts file, first to type and then to Record. Place the tag description to a logical place in the record, so that similar or related tags are together, i.e. clan logic related tags are put together, such as "Clan", "Stock", "Item" etc.
Example of the Record item with tag description:
Clan: {
name: 'Clan',
description: 'clan related functionality',
}