はじめに
この記事は モバイルファクトリー Advent Calendar 2019 の18日目の記事です。
こんにちは、ブロックチェーンチームのエンジニアの id:odan3240 です。
NestJS では @nestjs/swagger を用いることで、コントローラーの定義から OpenAPI (swagger) の仕様書を生成することができます。
このモジュールの使い方などは、先日の NestJS Advent Calendar 2019 に記事を投稿しましたので、そちらをご覧ください。
上のリンクの記事では、JSON 形式の OpenAPI の仕様書を取得する方法として http://localhost:3000/api-json にアクセスする方法を紹介しています。しかし、実際にアプリケーションを開発していく上でこの方法では CI のワークフローに組み込みにくいなどの問題があります。
この記事ではこの問題を解決する方法を紹介します。
問題の詳細
http://localhost:3000/api-json にアクセスする方法には、「バックエンドのサーバは起動している」という前提条件が必要になります。
この前提条件はかなり強い条件です。例えば MySQL などのデータストアを利用しているバックエンドのサーバを考えます。このサーバの起動時にデータストアにコネクションを張る設定をしていると、データストアの存在が前提条件に追加されます。つまり、バックエンドのサーバの他にデータストアも起動しておかないと http://localhost:3000/api-json にアクセスできず仕様書を取得することができません。
CI で OpenAPI の仕様書を出力してクライアントのコードを自動生成を行うワークフローを組んでいる場合、CI にデータストアを用意する必要があるため、手間がかかります。
解決方法
@nestjs/swagger は Controller の実装の定義から OpenAPI の仕様書を生成するモジュールですので、原理的にはサーバの起動なしに Controller のソースコードから仕様書を生成できるはずです。この記事で紹介する解決方法は、NestJS の DI 機構を用いて、これを実現します。
@nestjs/testing には Test.createTestingModule という関数があります。これを使うとクラスのモックを簡単に差し込むことができます。
Testing | NestJS - A progressive Node.js framework
具体的なコードは サンプルリポジトリ の src/openapi/generate.ts にあります。
ポイントは次の通りです。
Reflect.getMetadataを使ってAppModuleの Controller と Provider を取得する- これらを
createTestingModuleに引数に渡す- Provider は
useValueでモックする
- Provider は
- これらを
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { Test } from '@nestjs/testing'; import { AppModule } from '../app.module'; async function bootstrap(): Promise<void> { const controllers = Reflect.getMetadata('controllers', AppModule); const providers = Reflect.getMetadata('providers', AppModule); const mockedProviders = providers.map(provider => { return { provide: provider.name, useValue: {}, }; }); const testingModule = await Test.createTestingModule({ controllers: controllers, providers: mockedProviders, }).compile(); const app = await testingModule.createNestApplication(); const options = new DocumentBuilder() .setTitle('アドベントカレンダーサンプル') .build(); const document = SwaggerModule.createDocument(app, options); console.error(JSON.stringify(document, null, 2)); } bootstrap();
このファイルを ts-node で実行すると JSON 形式の OpenAPI の仕様書を取得することができます。
$ yarn ts-node src/openapi/generate.ts > /dev/null
{
"openapi": "3.0.0",
"info": {
"title": "アドベントカレンダーサンプル",
"description": "",
"version": "1.0.0",
"contact": {}
},
"tags": [],
"servers": [],
"components": {},
"paths": {
"/": {
"get": {
"operationId": "getHello",
"responses": {
"200": {
"description": ""
}
}
}
}
}
}
終わりに
NestJS の DI 機構を用いて Controller が依存するクラスをモックすることで、バックエンドのサーバを起動せずに OpenAPI の仕様書を取得する方法を紹介しました。これにより、データストアなどの他のミドルウェアに対する依存がなくなったため、CI のワークフローに組み込みやすくなりました。