はじめに
この記事は モバイルファクトリー 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 web 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 のワークフローに組み込みやすくなりました。