APIクライアントの手動作成はやめよう:OpenAPI Generator活用ガイド

Programming tutorial - IT technology blog
Programming tutorial - IT technology blog

午前2時14分の惨劇

枕元のスマホが午前2時14分に震えた。フロントエンドチームから、メインダッシュボードが完全にダウンし、至る所でundefinedエラーが出ているとの報告だった。45分間にわたる必死のログ調査の結果、犯人が判明した。バックエンドエンジニアがGoサービスのフィールド名をuser_idからuuidに変更していたが、TypeScriptのインターフェースは修正されないままだったのだ。この小さな不一致により、500エラーが12%急増。手動の「コピペ」ドキュメントが10日も古かったせいで、数時間の睡眠が奪われることになった。

自動化は単なるスピードアップのためだけではなく、正気を保つためのものでもある。APIクライアントやサーバーのボイラープレートを手動で書くことは、コアインフラにヒューマンエラーを招き入れるようなものだ。openapi-generatorを使用することで、OpenAPI Specification (OAS) を強制力のある「契約」へと変換できる。これにより、フロントエンド、バックエンド、モバイルアプリが常に全く同じ言語で通信することを保証できる。

クイックスタート:60秒以内に仕様書からコードへ

openapi.yamlファイルさえあれば、本番環境で使えるSDKを生成するために必要なものはすべて揃っている。重いツールチェーンをインストールする必要さえない。Dockerが面倒な作業をすべて引き受けてくれる。

フロントエンドチーム向けにTypeScriptのAxiosクライアントを生成するには、ターミナルで以下のコマンドを実行する:

docker run --rm -v "${PWD}:/local" \
    openapitools/openapi-generator-cli generate \
    -i /local/openapi.yaml \
    -g typescript-axios \
    -o /local/generated/sdk

各フラグの役割は以下の通りだ:

  • -i: 信頼できる唯一の情報源(YAMLまたはJSONの仕様書)。
  • -g: ターゲット言語。現在、C++からKotlinまで300種類以上のジェネレーターが用意されている。
  • -o: 新しいコードの出力先フォルダ。

数秒で、完全に型定義されたSDKが手に入る。これには各エンドポイントのメソッドや組み込みのエラーハンドリングが含まれており、すぐにインポートして使用できる。

「信頼できる唯一の情報源(Single Source of Truth)」戦略

マイクロサービスにおける最大の悩みは「ドキュメントの乖離(document drift)」だ。これは、APIの実際の挙動がドキュメントの記載と食い違ってしまう現象を指す。openapi-generatorは、仕様書をマスターレコード(正本)にすることで、この問題を解決する。仕様書にない変更は、コード内にも存在しないことになる。

設定ファイルによる微調整

単発のコマンドはテストには適しているが、本番環境では設定ファイルが必要だ。これにより、命名規則やパッケージ構造を強制できる。Java Spring Bootサーバーの場合、config.yamlは以下のようになる:

# config.yaml
artifactId: "order-service-api"
groupId: "com.techcorp.api"
apiPackage: "com.techcorp.api.controller"
modelPackage: "com.techcorp.api.model"
interfaceOnly: true
useTags: true

設定フラグを指定してジェネレーターを実行する:

openapi-generator-cli generate -i openapi.yaml -g spring -c config.yaml -o ./server

interfaceOnly: trueの設定は画期的だ。これによりインターフェースとデータ転送オブジェクト(DTO)のみが生成され、ビジネスロジックには一切手が加えられない。ジェネレーターがコアサービスのコードを上書きする心配をすることなく、API定義を毎日更新できるようになる。

GitHub Actionsによるワークフローの自動化

ローカルでの生成は良いスタートだが、真の力はCI/CDパイプラインで発揮される。SDKの更新を自動化するだけで、統合バグを40%削減したチームを私は見てきた。コードをプッシュする前にジェネレータースクリプトを実行することを、誰かに思い出させるような運用はすべきではない。

このGitHub Actionsのワークフローは、openapi.yamlが更新されるたびにトリガーされる。Python SDKをビルドし、プライベートなPyPIリポジトリに直接プッシュする。

# .github/workflows/sdk-gen.yml
name: Python SDKの生成
on:
  push:
    paths:
      - 'openapi.yaml'
jobs:
  build-sdk:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Pythonクライアントの生成
        uses: openapi-generators/openapitools-generator-action@v1
        with:
          generator: python
          config-file: python-config.yaml
          
      - name: PyPIへの公開
        env:
          TWINE_USERNAME: ${{ secrets.PYPI_USER }}
          TWINE_PASSWORD: ${{ secrets.PYPI_PASS }}
        run: |
          cd generated/python
          python setup.py sdist bdist_wheel
          twine upload dist/*

このセットアップにより、シームレスな体験が実現する。フロントエンドやモバイルのデベロッパーは、package.jsonrequirements.txtを更新するだけで最新の変更を取り込める。手動調整による摩擦は解消される。

現場で学んだ教訓

自動APIパイプラインを5年間運用してきた経験から、成功のための4つの重要なプラクティスを特定した。

1. Mustacheテンプレートによるカスタマイズ

デフォルトの出力がチームのリンティングルールに合わない場合がある。特定の企業用ヘッダーやカスタムログ用ラッパーをすべてのファイルに含める必要があるなら、生成されたコードを直接編集してはいけない。代わりに、内部のMustacheテンプレートをダウンロードして修正し、-tフラグを使ってジェネレーターにそのテンプレートを指定しよう。

2. バージョニングを破壊的変更として扱う

SDKが自動化されると、バージョニングはかつてないほど重要になる。openapi.yaml内の必須フィールドの名前を変更した場合は、必ずメジャーバージョンを上げなければならない。セマンティックバージョニング(SemVer)に従わないと、パイプラインが完了した瞬間に、下流のすべてのサービスが壊れることになる。

3. 生成前に仕様書をリントする

不適切な入力は、壊れたコードにつながる。ジェネレーターを実行する前に、Spectralのようなリンターを使って問題を特定しよう。説明文の欠落や、「camelCase」と「snake_case」の混在といった、SDKがプロフェッショナルに見えなくなるような不整合を指摘してくれる。

# バグになる前にエラーを検知する
npx @stoplight/spectral-cli lint openapi.yaml

4. 型を明示的に指定する

ジェネレーターは、JavaやGoのような厳密な型付けを持つ言語において、anyOfoneOfの扱いに苦労することがある。可能な限り、ジェネリックなオブジェクトは避けよう。OpenAPIの定義が具体的であればあるほど、結果として得られるIDEのオートコンプリートはチームにとって役立つものになる。

手動の統合に苦しむのはもうやめよう。まずは、1つの内部サービス用にシンプルなクライアントを生成することから始めてみてほしい。「ドキュメントの遅延」がないことでチームのスピードがどれほど上がるかを実感すれば、もう手動コーディングには戻れなくなるはずだ。

Share: