본문 바로가기
TypeScript

[Nest.js] 버전 별로 스웨거 관리

by yonikim 2023. 1. 11.
728x90

이번에 앱 리뉴얼로 인해 API 도 버전 분리를 하기로 결정했다. Header 에 버전을 추가하여 구분하기로 했는데 문제는 스웨거였다. 

 

JAVA 나 .NET 에서는 스웨거 버저닝 싱크 프로세스가 잘되어 있는데, Nest 에서는...

https://github.com/nestjs/swagger/issues/1810

우리와 같은 Needs 를 가진 사람들이 많으나, Nest 공식 사이트에선 답이 없는데... (열일해라 Nest...⭐️)

 

 

이 방법 저 방법 시도해보다가, 결국 아래와 같은 방법으로 해결했다. TypeScript 개발자 모두 화이팅...!

 

1. 버전 별로 Controller, Service, Module 을 분리한다.

▷ 폴더구조

├── src
│   ├── common
│   │   └── controllers
│   │       └── swagger.controller.ts
│   ├── v1
│   │   └── curation
│   │       ├── curation.controller.ts
│   │       ├── curation.service.ts
│   │       └── curation-v1.module.ts
│   ├── v2
│   │   └── curation
│   │       ├── curation.controller.ts
│   │       ├── curation.service.ts
│   │       └── curation-v2.module.ts
│   ├── curation.module.ts
│   └── main.ts
└── ...

 

 curation.module.ts

import { CurationV1Module } from "./v1/curation-v1.module";
import { CurationV2Module } from "./v2/curation-v2.module";
import { SwaggerController } from "./common/controller.ts/swagger.contoller";

@Module({
  imports: [CurationV1Module, CurationV2Module],
  controllers: [SwaggerController],
})
export class CurationModule {}

 

 

2. swagger.json 을 호출해주는 라우터를 만든다.

 swagger.controller.ts

import { Controller, Get, Param } from "@nestjs/common";
import * as fs from "fs";

@Controller("swagger")
export class SwaggerController {
  @Get(":version")
  async swagger(@Param("version") version: string) {
    try {
      const data = fs.readFileSync(
        `./curation-v${version}-swagger.json`,
      );
      return JSON.parse(data.toString());
    } catch (err) {
      console.log(err);
    }
  }
}

 

 

3. 버전 별 Module 로 스웨거 document 를 생성한다.

const domain = "curation";

const v1 = SwaggerModule.createDocument(
    app,
    options.setVersion("1").build(),
    {
      include: [CurationV1Module],
    },
);
fs.writeFileSync(`./${domain}-v1-swagger.json`, JSON.stringify(v1));

const v2 = SwaggerModule.createDocument(
    app,
    options.setVersion("2").build(),
    {
      include: [CurationV2Module],
    },
  );
fs.writeFileSync(`./${domain}-v2-swagger.json`, JSON.stringify(v2));

 

 

4. swaggerOptions.urls 에 2번에서 만든 라우터 주소를 버전 별로 추가한다.

SwaggerModule.setup(`${domain}/docs`, app, document, {
    explorer: true,
    swaggerOptions: {
      persistAuthorization: true,
      urls: [
        {
          name: "v1",
          url: `/${domain}/swagger/1`,
        },
        {
          name: "v2",
          url: `/${domain}/swagger/2`,
        },
      ],
    },
});

 

 main.ts

function createSwagger(
  app: INestApplication,
  description?: string,
): void {
  const domain = "curation";
  
  const options = new DocumentBuilder()
    .setTitle(`API Docs - ${domain.toUpperCase()}`)
    .setDescription(description || "")
    .addApiKey(
      { type: "apiKey", in: "header", name: AUTH_HEADER_KEY },
      AUTH_API_KEY_NAME,
    )
    .addApiKey(
      { type: "apiKey", in: "header", name: HEADER_VERSION_KEY },
      HEADER_VERSION_NAME,
    );

  const document = SwaggerModule.createDocument(app, options.build());

  const v1 = SwaggerModule.createDocument(
    app,
    options.setVersion("1").build(),
    {
      include: [CurationV1Module],
    },
  );
  fs.writeFileSync(`./${domain}-v1-swagger.json`, JSON.stringify(v1));

  const v2 = SwaggerModule.createDocument(
    app,
    options.setVersion("2").build(),
    {
      include: [CurationV2Module],
    },
  );
  fs.writeFileSync(`./${domain}-v2-swagger.json`, JSON.stringify(v2));

  SwaggerModule.setup(`${domain}/docs`, app, document, {
    explorer: true,
    swaggerOptions: {
      persistAuthorization: true,
      urls: [
        {
          name: "v1",
          url: `/${domain}/swagger/1`,
        },
        {
          name: "v2",
          url: `/${domain}/swagger/2`,
        },
      ],
    },
  });
}

 

 

5. app 을 실행해 보면 스웨거를 버전 별로 선택하여 확인할 수 있다. 

 

 

728x90