OpenAPI + OpenAPI Generatorで定義と実装を同時に管理しよう
OpenAPIとは
OpenAPIはHTTPベースのAPI定義を記述するための規格です。
元々はSwaggerという名前で世に知られていたのですが、現在はOpenAPI Initiativeによって管理されています。
JSONやYAMLで記述することができ、エンドポイント・メソッド・リクエストやレスポンスの形式、認証方法などを統一された形式で記述することができます。
標準規格のため、Swagger UI や Mock など補助ツールが豊富にあるため、開発効率・管理がしやすくテキストベースで決まった形のため可読性も高いのが特徴的です。
OpenAPI Generatorとは
OpenAPI仕様に基づいてコードを自動生成するツールです。OpenAPI Generatorを使用すると、APIクライアント、サーバースタブ、APIドキュメントなどを自動的に生成することができます。
手作業でコードを書く手間を省き、また、Java、Python、Ruby、Go、C#、TypeScriptなど、多くのプログラミング言語に対応しており、さまざまな環境で使用することができます。
https://openapi-generator.tech/
使い方
今回はサンプルとしてTypeScriptでのコード生成を例に挙げます。
まずはOpenAPI Generator CLIをNode環境に入れてください。
npm install @openapitools/openapi-generator-cli -g
次にOpenAPIの定義ファイルを作成します。(openapi.yaml)
openapi: 3.0.0 info: title: Simple API version: 1.0.0 paths: /users: get: summary: Get all users responses: '200': description: A list of users content: application/json: schema: type: array items: type: object properties: id: type: integer name: type: string
以下のコマンドを実行してTypeScriptのクライアントコードを生成します。
openapi-generator-cli generate -i openapi.yaml -g typescript-fetch -o ./typescript-client
-i openapi.yaml
:入力ファイルとしてopenapi.yaml
を指定します。-g typescript-fetch
:TypeScript (fetchベースのHTTPクライアント)を生成するためのコードジェネレーターを指定します。ここを変更すると生成する言語を切り替えることができます。-o ./typescript-client
:生成されたコードを出力するディレクトリを指定します。
正常に生成ができたらあとはご自身の環境にて呼び出すだけで問題ありません。
import { Configuration, DefaultApi } from './apis'; // APIクライアントの設定を行います const config = new Configuration({ basePath: 'http://localhost:3000', }); // APIクライアントのインスタンスを作成します const api = new DefaultApi(config); // ユーザーリストを取得する関数 async function getUsers() { try { const users = await api.usersGet(); console.log(users); } catch (error) { console.error('Error fetching users:', error); } } // ユーザーリストを取得します getUsers();
実装上の注意点
言語仕様は決まっているので、定義と実装が合致するのはとても良いことなのですが、
実際に実務で使用する場合はある程度仕様を理解しておかないとチーム開発する上で困ることも多いので
自分が注意した点をご紹介いたします。
additionalProperties: false をつけること
未定義のプロパティを許容しなくなります。意図しないパラメータが送信されることを防げるので
セキュリティ面も踏まえつけておくのが良いかと思います。
必須プロパティには required をつける
nullableかそうでないかをしっかり定義する様にしましょう。
TypeScriptの場合はundefinedになるかによって実装も若干異なりますので、API定義時点から気をつけておくと良いでしょう。
pathsにoperationId を追加する
識別IDとして利用されますが、コード生成時にメソッド名としても利用されます。
自分の場合${HTTPメソッド}${機能物理名} を記載し、camelCaseにて設定すること。
# GET /products operationId: getProducts # POST /products operationId: postProducts
pathsにtags を追加する
エンドポイント1つに1つのタグのみ定義する様にすると良いかと思います。
認証が必要なpathは isAuthorized を追加する
認証が必要なものにつけておくと、認証つきAPIを表現できます。
各エンドポイントに下記の様につけておきます。
post: security: - isAuthorized: []
APIの認証は定義全体で共有することができますので、たとえばOAuth認証にする場合は下記の様に記載します。
securityDefinitions: isAuthorized: type: oauth2 flow: accessCode authorizationUrl: 'https://example.com/authorize' tokenUrl: 'https://example.com/.well-known/jwks.json'
最後に
REST APIを採用するサービス開発はよくあると思いますが、実装とAPI設計を別々にしておくとよくずれ込みが起こったりするので、標準仕様として利用すると良いでしょう。