Just as the history of the Roman Empire was divided into eras - Kingdom, Republic, Empire - our API can have multiple versions. Versioning allows introducing changes without breaking existing integrations.
NestJS supports three versioning strategies:
/v1/legiones1// main.ts
2import { VersioningType } from '@nestjs/common';
3
4async function bootstrap() {
5 const app = await NestFactory.create(AppModule);
6
7 app.enableVersioning({
8 type: VersioningType.URI,
9 defaultVersion: '1',
10 });
11
12 await app.listen(3000);
13}After enabling versioning, controllers can declare their version:
1@Controller({ path: 'legiones', version: '1' })
2export class LegionV1Controller {
3 @Get()
4 findAll() {
5 return [{ name: 'Legio X', soldiers: 5000 }];
6 }
7}
8
9@Controller({ path: 'legiones', version: '2' })
10export class LegionV2Controller {
11 @Get()
12 findAll() {
13 return [{
14 name: 'Legio X Gemina',
15 soldiers: 5200,
16 province: 'Pannonia',
17 commander: 'Marcus Aurelius',
18 }];
19 }
20}1@Controller('legiones')
2export class LegionController {
3 @Version('1')
4 @Get()
5 findAllV1() {
6 return this.legionService.findAllSimple();
7 }
8
9 @Version('2')
10 @Get()
11 findAllV2() {
12 return this.legionService.findAllDetailed();
13 }
14}When the API has multiple versions, it is worth creating separate Swagger documents for each version:
1async function bootstrap() {
2 const app = await NestFactory.create(AppModule);
3
4 app.enableVersioning({
5 type: VersioningType.URI,
6 defaultVersion: '1',
7 });
8
9 // Document for version 1
10 const configV1 = new DocumentBuilder()
11 .setTitle('Imperium API v1')
12 .setDescription('Republic Era - basic endpoints')
13 .setVersion('1.0')
14 .addBearerAuth()
15 .build();
16
17 const documentV1 = SwaggerModule.createDocument(app, configV1, {
18 include: [LegionV1Module, ProvinceV1Module],
19 });
20 SwaggerModule.setup('api/v1/docs', app, documentV1);
21
22 // Document for version 2
23 const configV2 = new DocumentBuilder()
24 .setTitle('Imperium API v2')
25 .setDescription('Empire Era - extended endpoints')
26 .setVersion('2.0')
27 .addBearerAuth()
28 .build();
29
30 const documentV2 = SwaggerModule.createDocument(app, configV2, {
31 include: [LegionV2Module, ProvinceV2Module],
32 });
33 SwaggerModule.setup('api/v2/docs', app, documentV2);
34
35 await app.listen(3000);
36}Alternatively, you can use tags to differentiate versions within a single document:
1@ApiTags('Legiones v1')
2@Controller({ path: 'legiones', version: '1' })
3export class LegionV1Controller {
4 @ApiOperation({ summary: '[v1] Get legions' })
5 @Get()
6 findAll() {
7 return this.service.findAllV1();
8 }
9}
10
11@ApiTags('Legiones v2')
12@Controller({ path: 'legiones', version: '2' })
13export class LegionV2Controller {
14 @ApiOperation({ summary: '[v2] Get legions with details' })
15 @Get()
16 findAll() {
17 return this.service.findAllV2();
18 }
19}A good practice is to mark versions in endpoint descriptions:
1@ApiOperation({
2 summary: 'Get list of legions',
3 description: `
4 **Version 2.0** - Extended response with location and commander.
5 Replaces the v1 endpoint which returned only basic data.
6 Version v1 will be deprecated in Q3 2025.
7 `,
8 deprecated: false,
9})
10@Get()
11findAll() {}When an old version of an endpoint is being retired:
1@ApiOperation({
2 summary: '[DEPRECATED] Get legions',
3 description: 'Use /v2/legiones instead of this endpoint',
4 deprecated: true,
5})
6@Get()
7findAllV1() {
8 return this.service.findAllV1();
9}Configure API versioning with two Swagger documents: