ほとんどのAPIのバグはコーディングミスではありません。それらは合意の誤りです。フロントエンドはuserIdを期待していたのに、バックエンドはuser_idを送信し、誰もQAまで気づきませんでした。スペックファーストAPI開発は、契約を最後に文書化するのではなく、最初に作成することでこれを解決します。
このガイドでは、小さなOpenAPIスペックを手作業で記述し、その単一ファイルを使って、サーバーコードが存在する前にモック、テスト、ドキュメントを生成します。このアプローチは、スペック駆動開発、デザインファースト、契約ファーストといったいくつかの呼び名で知られています。これらはすべて同じ考え方を指しています。まずインターフェースに合意し、それに基づいて構築する、というものです。
スペックファーストAPI開発とは
スペックファーストAPI開発とは、エンドポイントを実装する前に、通常OpenAPIドキュメントである機械可読な契約を作成することを意味します。その契約には、すべてのパス、パラメータ、リクエストボディ、レスポンスの形式、ステータスコードが記述されます。それが存在すれば、APIに触れるすべての人にとって信頼できる情報源となります。
スペックはコードの後を追うドキュメントではありません。それがリードします。フロントエンドチームは、そのスペックから生成されたモックに対して構築します。QAはそれに対してテストを作成します。バックエンドはそれを満たすように実装します。これら3者がすべて同じファイルに合意すると、統合は当てずっぽうの作業ではなくなります。
これは通常の順序を逆転させます。コードファースト開発では、ハンドラを記述し、その後で(多くの場合手作業で、スプリント内で時代遅れになることも多い)それらを記述します。デザインファーストのワークフローでは、記述が最初に来て、コードがそれに従います。その単一の変更により、ほとんどの統合問題がプロジェクトの初期段階に移行し、そこで安価に修正できるようになります。
スペックファーストとコードファーストのライフサイクル
2つのアプローチは同じエンドポイントを生成します。それらは、いつ問題が表面化するか、そして誰が並行して作業を開始できるかという点で異なります。

右側の列がその成果です。契約が最初に存在することで、3つのチームが互いにブロックし合うことをやめ、共有された定義に基づいて構築を開始します。
OpenAPIの作業例
小さな/usersエンドポイントを段階的に設計してみましょう。各部分が明確になるように少しずつ構築し、その後完全なファイルを組み立てます。
ドキュメントヘッダーから始めます。これによりOpenAPIのバージョンと基本的なメタデータが宣言されます。
openapi: 3.0.3
info:
title: Users API
version: 1.0.0
servers:
- url: https://api.example.com/v1
次に、componentsの下にUserスキーマを定義します。一度定義すればどこでも参照できるため、リクエストとレスポンス間で形式の一貫性が保たれます。
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
さて、GET /usersパスを追加します。これはユーザーを一覧表示し、limitクエリパラメータをサポートします。レスポンスがUserスキーマを再定義するのではなく、$refを使って再利用している点に注目してください。
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 /users操作を追加します。リクエストボディはクライアントが送信すべきものを定義し、201レスポンスは作成されたレコードを返します。
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
これは完全で有効な契約です。すべてのフィールドを命名し、emailを必須とマークし、limitを100に制限し、成功とエラーの両方のレスポンスを宣言しています。まだ誰一人としてサーバーコードを書いていませんが、合意は確定しています。
スペックからモック、テスト、ドキュメントを生成する
スペックを最初に書く理由は、そのレバレッジ(てこの原理)にあります。一つのファイルが、通常チームが別々に構築する3つの成果物を動かします。
モック。モックサーバーはあなたのスペックを読み込み、各スキーマに一致するサンプルレスポンスを返します。format: uuidとformat: emailのヒントにより、ツールは現実的なサンプルデータを生成できます。フロントエンドはGET /usersを呼び出し、実際のハンドラが存在するずっと前の初日から、整形されたユーザーの配列を取得します。スペックが変更されると、モックもそれに合わせて変更されます。
テスト。スペックは必須フィールド、型、ステータスコードを宣言するため、テストオラクルとしても機能します。契約テストは、POST /usersの実際のレスポンスがUserスキーマに一致するボディを持つ201を返し、emailが欠落している場合は400を返すことをアサートします。あなたアサーションを考案しているわけではありません。あなたは実装がすでに合意した内容と一致しているかを確認しているのです。
ドキュメント。APIリファレンスドキュメントはスペックから直接レンダリングされます。ドキュメントで目にするすべてのエンドポイント、パラメータ、例は、同じYAMLから生成されます。同期を維持するための2つ目のコピーは存在しないため、ドキュメントが契約から逸脱することはありません。
これはまた、スペックファーストがGitネイティブAPIワークフローとうまく連携する理由でもあります。スペックはプレーンテキストファイルであるため、契約へのすべての変更はプルリクエストでレビュー可能な差分として表示されます。レビュー担当者は、誰かがemailの名前を変更したか、必須フィールドを削除したかを、出荷前に確認できます。
Apidogでの実施
Apidogはスペックファーストモードを通じて、これをエンドツーエンドでサポートします。OpenAPIファイルをエクスポートとして扱うのではなく、ファイルをプロジェクトとして扱います。YAMLを直接編集すると、ワークスペースの残りの部分がそれに追従します。

/usersスペックをエディタに記述または貼り付けます。Apidogはそれを解析し、両方の操作に対してすぐにモックサーバーを立ち上げるので、フロントエンドはそれらを呼び出すことができます。生成されたドキュメントは入力するにつれて更新されます。実装を検証する準備ができたら、スペックの操作を実際のバックエンドに対するテストケースとして実行し、レスポンスがスキーマに一致することを確認します。
これを正直に保つのは、双方向のGit同期です。あなたのスペックはリポジトリに存在し、変更は双方向に流れます。エディタでYAMLを編集してプッシュすると、Apidogがそれを検知します。Apidogで編集すると、その変更はチームがレビューできるコミットとして記録されます。契約が同時に2つの場所に存在することはありません。純粋なデザインファーストのアプローチとこのアプローチの位置づけについてより深く比較したい場合は、Apidogにおけるスペックファースト vs デザインファーストを参照してください。
スペックファーストチェックリスト
スペックを構築準備ができたと呼ぶ前に、これらを使用してください。
- スペックはOpenAPIスキーマに対してエラーなしで検証されます。
- すべてのエンドポイントは、成功と少なくとも1つのエラーレスポンスを宣言します。
- 共有される形式は
components/schemasに存在し、コピーされず$refで参照されます。 - 必須フィールドは
requiredとマークされ、フォーマット(uuid、email、date-time)は適用される場所で設定されます。 - スペックファイルはバージョン管理にコミットされ、プルリクエストでレビューされます。
- モックサーバーはスペックから実行され、フロントエンドはそれにアクセスできます。
- 契約テストは、実際のレスポンスを宣言されたスキーマに対してチェックします。
- 公開されたドキュメントは、手作業で維持されたコピーなしで、同じファイルからレンダリングされます。
すべてのチェックボックスがオンになっていれば、あなたのチームは3つの推測ではなく、1つの合意に基づいて並行して構築できます。
FAQ
スペックファーストAPI開発はデザインファーストと同じですか?
ほとんどの場合、同じです。「デザインファースト」と「契約ファースト」は同じ原則、つまり実装の前にインターフェースを記述するという原則を表しています。「スペックファースト」は、OpenAPIスペックファイルが最初に扱う具体的な成果物であるため、最も文字通りの名称です。実際には、これらの用語は区別なく使われています。
YAMLを手作業で書く必要がありますか?
いいえ。ビジュアルエディタでスペックを作成し、YAMLを生成させることもできますし、お好みであれば直接YAMLを記述することもできます。重要なのは入力するフォーマットではなく、コードを書く前に契約が存在し、合意されていることです。多くのチームでは、ビジュアルで下書きし、レビュー中にYAMLで洗練するという両方を組み合わせています。
スペックとコードが乖離するのをどう防ぎますか?
スペックを信頼できる唯一の情報源(Source of Truth)として、それを強制します。スペックをGitに保管し、変更が差分としてレビューされるようにします。CIで契約テストを実行し、レスポンスがスキーマと一致しなくなった場合にビルドが失敗するようにします。双方向同期により、どちらの場所での編集も整合性が保たれ、最も一般的な乖離の原因を取り除きます。
スペックファーストAPI開発は、順序の小さな変更が結果に大きな変化をもたらします。契約を書き、それに合意し、それに基づいて構築します。この一連のプロセス全体を試したい場合は、Apidogでスペックファーストモードを開き、自分のリポジトリを指してください。
