DEV Community

Cover image for 仕様優先API開発とは
Akira
Akira

Posted on • Originally published at apidog.com

仕様優先API開発とは

ほとんどのAPIバグはコーディングミスではなく、契約の不一致です。フロントエンドは userId を期待しているのに、バックエンドは user_id を返す。このような差分はQAや結合テストまで見つからないことがあります。スペックファーストAPI開発では、実装前にOpenAPIスペックを作成し、チーム全員が同じ契約に基づいて開発します。

今すぐApidogを試す

このガイドでは、小さな /users APIを例に、OpenAPIスペックを手作業で作成し、その1つのファイルからモック、テスト、ドキュメントを生成する流れを整理します。このアプローチは、スペック駆動開発、デザインファースト、契約ファーストとも呼ばれます。共通する考え方は、先にインターフェースへ合意し、その後に実装することです。

スペックファーストAPI開発とは

スペックファーストAPI開発では、エンドポイントを実装する前に、OpenAPIなどの機械可読な契約を作成します。

この契約には、次の内容を明示します。

  • パス
  • HTTPメソッド
  • クエリパラメータ
  • リクエストボディ
  • レスポンス形式
  • ステータスコード
  • 共通スキーマ
  • 必須フィールド
  • データ型とフォーマット

スペックは、コードの後に書く補助ドキュメントではありません。開発の起点です。

実装前にスペックがあると、各チームは並行して作業できます。

  • フロントエンド: スペックから生成されたモックAPIに対して実装する
  • QA: スペックを元に契約テストを作成する
  • バックエンド: スペックを満たすように実装する
  • ドキュメント: 同じスペックからAPIリファレンスを生成する

コードファーストでは、実装後にドキュメントを書き、そこで不一致が見つかることがあります。スペックファーストでは、先に契約をレビューするため、統合時の手戻りを減らせます。

スペックファーストとコードファーストのライフサイクル

2つのアプローチは、最終的には同じAPIを作れます。違いは、問題が見つかるタイミングと、誰がいつ作業を開始できるかです。

スペックファーストでは、契約が最初に存在します。そのため、フロントエンド、バックエンド、QAが互いを待たず、同じ定義に基づいて作業を開始できます。

OpenAPIの作業例

ここでは、/users エンドポイントをOpenAPIで設計します。

作成するAPIは次の2つです。

  • GET /users: ユーザー一覧を取得する
  • POST /users: ユーザーを作成する

1. OpenAPIドキュメントのヘッダーを定義する

まず、OpenAPIのバージョン、API名、バージョン、サーバーURLを定義します。

openapi: 3.0.3

info:
  title: Users API
  version: 1.0.0

servers:
  - url: https://api.example.com/v1
Enter fullscreen mode Exit fullscreen mode

この時点ではエンドポイントはまだありません。API全体のメタデータだけを定義しています。

2. 共通のUserスキーマを定義する

次に、components.schemasUser スキーマを作成します。

components:
  schemas:
    User:
      type: object
      required:
        - id
        - email
        - createdAt
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        name:
          type: string
        createdAt:
          type: string
          format: date-time
Enter fullscreen mode Exit fullscreen mode

ポイントは、レスポンスごとに同じ構造をコピーしないことです。

共通スキーマを components に置き、各エンドポイントでは $ref で参照します。これにより、フィールド名や型のズレを防げます。

3. GET /usersを定義する

GET /users は、ユーザー一覧を返すエンドポイントです。

ここでは、limit クエリパラメータを受け取り、最大値を 100 に制限します。

paths:
  /users:
    get:
      summary: List users
      operationId: listUsers
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        "200":
          description: A list of users
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/User"
Enter fullscreen mode Exit fullscreen mode

items#/components/schemas/User を参照しているため、レスポンスは User オブジェクトの配列になります。

4. POST /usersを定義する

次に、ユーザー作成用の POST /users を追加します。

paths:
  /users:
    post:
      summary: Create a user
      operationId: createUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
                name:
                  type: string
      responses:
        "201":
          description: User created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "400":
          description: Invalid request body
Enter fullscreen mode Exit fullscreen mode

この定義で、クライアントが送信すべきリクエストボディと、成功時・失敗時のレスポンスが明確になります。

5. 完全なOpenAPIファイル

上記を1つにまとめると、次のようになります。

openapi: 3.0.3

info:
  title: Users API
  version: 1.0.0

servers:
  - url: https://api.example.com/v1

paths:
  /users:
    get:
      summary: List users
      operationId: listUsers
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        "200":
          description: A list of users
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/User"

    post:
      summary: Create a user
      operationId: createUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
                name:
                  type: string
      responses:
        "201":
          description: User created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "400":
          description: Invalid request body

components:
  schemas:
    User:
      type: object
      required:
        - id
        - email
        - createdAt
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        name:
          type: string
        createdAt:
          type: string
          format: date-time
Enter fullscreen mode Exit fullscreen mode

この時点で、サーバーコードはまだありません。しかし、API契約としては次の内容が確定しています。

  • email は必須
  • id はUUID
  • createdAt は日時
  • limit は最大100
  • 作成成功時は 201
  • 不正なリクエストは 400
  • ユーザーオブジェクトの形式は共通スキーマで管理する

スペックからモック、テスト、ドキュメントを生成する

スペックファーストの実用的なメリットは、1つのOpenAPIファイルから複数の成果物を生成できることです。

モックを生成する

モックサーバーはOpenAPIスペックを読み取り、スキーマに合うサンプルレスポンスを返します。

たとえば、GET /users のレスポンススキーマには次の情報があります。

type: array
items:
  $ref: "#/components/schemas/User"
Enter fullscreen mode Exit fullscreen mode

User スキーマには、さらに次のフォーマット指定があります。

id:
  type: string
  format: uuid
email:
  type: string
  format: email
createdAt:
  type: string
  format: date-time
Enter fullscreen mode Exit fullscreen mode

このため、ツールはUUID、メールアドレス、日時として妥当なサンプルデータを生成できます。

フロントエンドは、バックエンド実装が完了する前に、モックAPIへリクエストして画面実装を進められます。

契約テストを作成する

スペックには、期待されるレスポンス形式とステータスコードが定義されています。

そのため、実装後は次のような観点でテストできます。

  • POST /users が成功時に 201 を返す
  • レスポンスボディが User スキーマに一致する
  • email が欠落したリクエストで 400 を返す
  • GET /users のレスポンスが配列である
  • 各要素が User スキーマに一致する

これは、テスト仕様をゼロから考えるのではなく、合意済みの契約を検証する作業です。

ドキュメントを生成する

APIリファレンスもOpenAPIスペックから生成できます。

ドキュメントに表示される次の情報は、同じYAMLから作られます。

  • エンドポイント一覧
  • メソッド
  • パラメータ
  • リクエストボディ
  • レスポンス例
  • ステータスコード
  • スキーマ定義

手作業で別ドキュメントを管理しないため、実装・テスト・ドキュメントのずれを減らせます。

この考え方は、GitネイティブAPIワークフローとも相性が良いです。スペックはプレーンテキストなので、API契約への変更はプルリクエストの差分としてレビューできます。

レビュー担当者は、たとえば次のような変更を出荷前に確認できます。

  • email が別名に変わっていないか
  • 必須フィールドが削除されていないか
  • ステータスコードが変わっていないか
  • レスポンススキーマに破壊的変更がないか

Apidogでの実施

Apidogは、スペックファーストモードを通じて、このワークフローをサポートします。

OpenAPIファイルを単なるエクスポート結果として扱うのではなく、プロジェクトの中心として扱います。YAMLを編集すると、モック、ドキュメント、テストの作業もその契約に基づいて進められます。

実装の流れは次のようになります。

  1. /users のOpenAPIスペックを作成する
  2. ApidogのエディタにYAMLを貼り付ける、または直接編集する
  3. スペックを解析してエンドポイントを確認する
  4. モックサーバーを起動する
  5. フロントエンドからモックAPIを呼び出す
  6. 生成されたドキュメントを確認する
  7. バックエンド実装後、実レスポンスがスキーマに一致するか検証する
  8. スペック変更をGitでレビューする

双方向のGit同期を使うと、スペックをリポジトリに置いたまま管理できます。エディタでYAMLを編集してプッシュするとApidog側に反映され、Apidogでの編集もチームがレビューできる変更として扱えます。

スペックファーストとデザインファーストの位置づけを比較したい場合は、Apidogにおけるスペックファースト vs デザインファーストも参照してください。

スペックファーストチェックリスト

スペックを実装に進めてよい状態か確認するには、次のチェックリストを使います。

  • OpenAPIスキーマとしてエラーなく検証できる
  • すべてのエンドポイントに成功レスポンスがある
  • すべてのエンドポイントに少なくとも1つのエラーレスポンスがある
  • 共通オブジェクトは components.schemas に定義されている
  • 共通スキーマはコピーせず $ref で参照している
  • 必須フィールドは required に明記されている
  • uuidemaildate-time などの format が必要な箇所に設定されている
  • クエリパラメータのデフォルト値や最大値が定義されている
  • スペックファイルがGitにコミットされている
  • スペック変更がプルリクエストでレビューされる
  • モックサーバーがスペックから起動できる
  • フロントエンドがモックAPIへアクセスできる
  • 契約テストで実レスポンスをスキーマに照合している
  • 公開ドキュメントが同じスペックから生成されている

すべて満たしていれば、チームは別々の推測ではなく、1つの合意に基づいて並行開発できます。

FAQ

スペックファーストAPI開発はデザインファーストと同じですか?

ほとんどの場合、同じ原則を指します。

「デザインファースト」「契約ファースト」「スペックファースト」はいずれも、実装前にインターフェースを定義する考え方です。

その中でも「スペックファースト」は、OpenAPIスペックファイルを最初に扱う成果物として明確に示す言葉です。実務では、これらの用語が近い意味で使われることが多いです。

YAMLを手作業で書く必要がありますか?

必須ではありません。

ビジュアルエディタでAPIを設計し、OpenAPI YAMLを生成しても構いません。逆に、エディタで直接YAMLを書いても問題ありません。

重要なのは、入力方法ではなく、コードを書く前に契約が存在し、チームで合意されていることです。

多くのチームでは、次のように併用します。

  • 初期設計はビジュアルエディタで作る
  • レビュー時はYAML差分を見る
  • 細かい修正はYAMLで直接行う
  • 最終的な契約はGitで管理する

スペックとコードが乖離するのをどう防ぎますか?

スペックを信頼できる唯一の情報源として扱い、それをテストとレビューで強制します。

実践することは次の3つです。

  1. スペックをGitで管理する
  2. スペック変更をプルリクエストでレビューする
  3. CIで契約テストを実行する

レスポンスがスキーマと一致しなくなった場合にビルドを失敗させれば、実装と契約のずれを早期に検出できます。

スペックファーストAPI開発は、作業順序を少し変えるだけです。先に契約を書き、合意し、それに基づいて実装します。この流れを試すには、Apidogでスペックファーストモードを開き、自分のリポジトリを指定してください。

Top comments (0)