nestjs/swagger 3系（Swagger2.0）⇒4系（Swagger3.0(OpenAPI)）へのバージョンアップ

「[Nest.jsのSwaggerが3.0（OpenAPI）に対応するぞ！！！](https://qiita.com/teracy55/items/1cf4bdd75a1b80b459da)」でも記載しましたが、nestjs/swaggerの4系がリリースされ、正式にSwagger3.0（OpenAPI）に対応しました！！！

今回は、nestjs/swaggerの3系から4系へのバージョンアップしたときの手順を備忘録がてら書いておこうと思います。


# nestjs/swaggerのバージョンアップ

nestjs/swaggerの最新版をインストールします

```
npm i --save @nestjs/swagger@latest 
```

# コードの修正

## アノテーションの変更

[リファレンス](https://docs.nestjs.com/recipes/swagger#migration-to-40)にも書いてありますが、いくつかアノテーションが変更になっているため、置換します。

>The following decorators have been changed/renamed:
>
>- @ApiModelProperty is now @ApiProperty
>- @ApiModelPropertyOptional is now @ApiPropertyOptional
>- @ApiResponseModelProperty is now @ApiResponseProperty
>- @ApiImplicitQuery is now @ApiQuery
>- @ApiImplicitParam is now @ApiParam
>- @ApiImplicitBody is now @ApiBody
>- @ApiImplicitHeader is now @ApiHeader
>- @ApiOperation({ title: 'test' }) is now@ApiOperation({ summary: 'test' })
>- @ApiUseTags is now @ApiTags

`@ApiOperation`だけは、なぜかオプションのプロパティ名が変更になっているため注意です。

## DocumentBuilderのオプション変更

DocumentBuilderにて、いくつかの関数が変更／追加／削除されているため対応します。

>DocumentBuilder breaking changes (updated method signatures):
>
>- addTag
>- addBearerAuth
>- addOAuth2
>- setContactEmail is now setContact
>- setHost has been removed
>- setSchemes has been removed

>The following methods have been added:
>
>- addServer
>- addApiKey
>- addBasicAuth
>- addSecurity
>- addSecurityRequirements


### setGlobalPrefixを使っている場合

[リファレンス](https://docs.nestjs.com/recipes/swagger#global-prefix)に記載がありますが、GlobalPrefixを使っている場合はオプションで`ignoreGlobalPrefix: true`を指定する必要があります。

これを指定しないと、`http://localhost:3000/api/api/hello`みたいにプレフィクスが重複します。

↓修正例

```
const apiPrefix = '/api/v1';
app.setGlobalPrefix(apiPrefix);

const options = new DocumentBuilder()
    ・・・
    .addServer(apiPrefix)
    .build();

SwaggerModule.createDocument(app, options, { ignoreGlobalPrefix: true });
```

## プラグインの設定

[プラグイン](https://docs.nestjs.com/recipes/swagger#plugin)がいくつか使えるようになりました。
これにより、DTOクラスにアノテーションを指定する必要がなくなったり、`class-validator`のいくつかのアノテーションをSwaggerに取り込んでくれるようになります。

```nestjs-cli.json
{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "plugins": [
      {
        "name": "@nestjs/swagger/plugin",
        "options": {
          "classValidatorShim": true
        }
      }
    ]
  }
}
```

オプションとして、以下を指定することができます

- `dtoFileNameSuffix`：dtoファイルのサフィックスを指定できます（デフォルトは`.dto.ts`,`.entity.ts`）
- `controllerFileNameSuffix`：コントローラーファイルのサフィックスを指定できます（デフォルトは`.controller.ts`）
- `classValidatorShim`：[class-validator](https://www.npmjs.com/package/class-validator)のアノテーションを有効にする場合はtrue（デフォルトはfalse）
    - バリデーションについては[こちら](https://docs.nestjs.com/techniques/validation)を参照


# [openapi-generator](https://github.com/OpenAPITools/openapi-generator)を使ってAPIクライアントを生成している場合

nestjs/swagger3系で出力されるSpecファイルを使っていた場合、生成されるAPIクライアントの関数名はデフォルトで`{パス}{メソッド}`でした。

4系になり、operationIdにコントローラーの関数名がセットされ、その名称でAPIクライアントが生成されるため、クライアントサイドで`function not found`が大量に発生しました。。。

【バージョンごとの出力例】

|バージョン|URL|メソッド|コントローラーの関数名|出力されるAPIクライアント|
|---|---|---|---|---|
|3系|/hello/world|GET|getMessage()|helloWorldGet()|
|4系|/hello/world|GET|getMessage()|getMessage()|




## 対応

1. `@ApiOperation`で`operationId`を指定する
2. コントローラーの関数名を元のAPIクライアント名に変更
3. コントローラーの関数名でクライアントサイドの呼び出し箇所を置換する

幸いまだリリース前のプロジェクトで大幅な変更も受け付けられるので3を採用しました。


# バグ？

DTOファイルでエンティティファイルをインポートすると、リファレンスエラーになりました。
対象ファイル（ビルド後）は↓のような感じで、`test_entity_1.default`がないと怒られます。

```test.dto.js
Object.defineProperty(exports, "__esModule", { value: true });
const openapi = require("@nestjs/swagger");
const class_validator_1 = require("class-validator");
class TestDto {
}
__decorate([
    openapi.ApiProperty({ required: true, type: () => require("../models/test.entity").default }),
    __metadata("design:type", test_entity_1.default)
], TestDto.prototype, "apply", void 0);
```

よく見ると`require("../models/test.entity")`をtest_entity_1に設定する処理がありません。。。

とりあえず、`@ApiProperty({type: Test})`のように直接型を指定すれば回避できますが、イケてないですね。。。
そもそもエンティティファイルをDTOファイルでインポートするな！ということなんでしょうか？

今後のバージョンアップに期待しましょう。




# まとめ

割と簡単にバージョンアップができました。
今回のバージョンアップでOpenAPIに対応しただけじゃなく、DTOやレスポンス等の一部のアノテーションを省略できるようになったのはかなり大きいと思います。