取り組みの背景 — なぜ API ファースト開発が重要なのか
モダンなソフトウェア開発において、フロントエンドとバックエンドを並行開発する体制は当たり前になりましました。しかし、口頭やドキュメントだけで API の仕様を共有していると、実装の食い違いが発生し、統合テストの段階で大量のバグが噴出するという問題を多くのチームが経験しています。
API ファースト開発とは、まず API の仕様(コントラクト)を定義し、それを単一の信頼できる情報源(Single Source of Truth)として開発を進める手法です。OpenAPI(旧 Swagger)仕様を中心に据えることで、バックエンドのスタブ生成、フロントエンドの型定義、テストケースの自動生成、そして API ドキュメントの自動公開まで、すべてを一つの仕様ファイルから派生させることが可能になります。
Antigravity の AI エージェントは、この API ファースト開発のワークフローと極めて相性が良い設計です。ここではOpenAPI 仕様の設計から始めて、コード自動生成、テスト、ドキュメント生成、CI/CD パイプラインへの統合まで、実務で使える完全なワークフローを体系的に解説します。
なお、API 設計の基礎やサーバーレス構成に興味のある方は Antigravity でサーバーレス API を効率的に構築 — Hono + Cloudflare Workers 実践ガイド もあわせてご参照ください。
OpenAPI 仕様の基礎と設計原則
OpenAPI 3.1 の構造
OpenAPI 3.1 は JSON Schema との完全互換を達成したバージョンであり、2026年現在のデファクトスタンダードです。基本構造は以下の通りです。
# openapi.yaml — プロジェクトの API 仕様書
openapi: "3.1.0"
info:
title: "My SaaS API"
version: "1.0.0"
description: "ユーザー管理と課金を担うコア API"
servers:
- url: "https://api.example.com/v1"
description: "本番環境"
- url: "http://localhost:3000/v1"
description: "ローカル開発"
paths:
/users:
get:
operationId: listUsers
summary: "ユーザー一覧を取得"
parameters:
- name: page
in: query
schema:
type: integer
default: 1
- name: limit
in: query
schema:
type: integer
default: 20
maximum: 100
responses:
"200":
description: "成功"
content:
application/json:
schema:
$ref: "#/components/schemas/UserListResponse"
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
UserListResponse:
type: object
properties:
users:
type: array
items:
$ref: "#/components/schemas/User"
total:
type: integer
page:
type: integer設計原則:「API を使う側」の視点で考える
良い API 設計の鍵は、実装者ではなく利用者の視点に立つことです。Antigravity のエージェントに仕様書作成を依頼する際も、以下の原則をプロンプトに含めると品質が大幅に向上します。
- リソース指向: URL パスは名詞(
/users,/orders)で、動詞は HTTP メソッドで表現する - 一貫性のある命名:
operationIdはlistUsers,getUser,createUserのように動詞 + リソース名で統一する - エラーレスポンスの標準化: RFC 7807(Problem Details)形式で全エンドポイント共通のエラー構造を定義する
- ページネーション: カーソルベースまたはオフセットベースを統一し、
components/parametersで再利用する
Antigravity で OpenAPI 仕様を効率的に作成する
AI エージェントによる仕様書生成
Antigravity の Manager Surface からエージェントに仕様書の生成を依頼する方法が最も効率的です。以下のようなプロンプトで、実用的な仕様書を生成できます。
# Antigravity プロンプト例
以下の要件に基づいて OpenAPI 3.1 仕様書を YAML で作成してください。
## サービス概要
- ECサイトの商品管理 API
- 認証は Bearer トークン(JWT)
- ページネーションはカーソルベース
## エンドポイント
1. 商品一覧(フィルタ・ソート対応)
2. 商品詳細
3. 商品作成(管理者のみ)
4. 商品更新(管理者のみ)
5. 商品削除(管理者のみ)
## 制約
- エラーレスポンスは RFC 7807 準拠
- 全レスポンスに rate-limit ヘッダーを含める
- components/schemas を活用してスキーマを再利用するAntigravity のエージェントはコンテキスト内の既存コードを参照するため、プロジェクト内に既にモデル定義(Prisma スキーマや TypeORM エンティティなど)がある場合、自動的にそれらと整合性のある仕様を生成します。
Editor View でのインライン編集
仕様書の細部を調整するには、Editor View のインライン AI コマンドが便利です。仕様ファイルを開いた状態で、特定のパスやスキーマにカーソルを当て、Ctrl+I(インラインコマンド)を起動します。
# インラインコマンドの例
「このエンドポイントに rate limiting のレスポンスヘッダー
(X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset)を追加して」AI がスキーマの文脈を理解した上でヘッダー定義を components/headers に追加し、各レスポンスから $ref で参照する構造を自動生成します。
コード自動生成パイプラインの構築
TypeScript バックエンド(Hono + Zod)の自動生成
OpenAPI 仕様から TypeScript のサーバーコードを生成するパイプラインを構築します。ここでは openapi-typescript と openapi-zod-client を組み合わせるアプローチを紹介します。
# 必要なパッケージのインストール
npm install openapi-typescript openapi-zod-client -D
# 型定義の生成
npx openapi-typescript openapi.yaml -o src/generated/api-types.ts
# Zod スキーマの生成
npx openapi-zod-client openapi.yaml -o src/generated/api-schemas.ts生成された型定義を Hono のルーターで活用する実装例です。
// src/routes/users.ts
import { Hono } from "hono";
import { zValidator } from "@hono/zod-validator";
import { listUsersQuerySchema, createUserBodySchema } from "../generated/api-schemas";
import type { paths } from "../generated/api-types";
// 型安全なレスポンス型
type ListUsersResponse =
paths["/users"]["get"]["responses"]["200"]["content"]["application/json"];
const users = new Hono();
users.get(
"/",
zValidator("query", listUsersQuerySchema),
async (c) => {
const { page, limit } = c.req.valid("query");
// DB からユーザーを取得(Prisma の例)
const [items, total] = await Promise.all([
prisma.user.findMany({
skip: (page - 1) * limit,
take: limit,
orderBy: { createdAt: "desc" },
}),
prisma.user.count(),
]);
// レスポンスは ListUsersResponse 型と互換
const response: ListUsersResponse = {
users: items,
total,
page,
};
return c.json(response);
}
);
export default users;フロントエンド API クライアントの自動生成
フロントエンド側では openapi-fetch を使うことで、型安全な API クライアントを自動生成できます。
// src/lib/api-client.ts
import createClient from "openapi-fetch";
import type { paths } from "../generated/api-types";
// 型安全な API クライアント
const client = createClient<paths>({
baseUrl: process.env.NEXT_PUBLIC_API_URL,
headers: {
Authorization: `Bearer ${getAccessToken()}`,
},
});
// 使用例 — パス・パラメータ・レスポンスすべてが型安全
async function fetchUsers(page: number = 1) {
const { data, error } = await client.GET("/users", {
params: {
query: { page, limit: 20 },
},
});
if (error) {
// error の型も OpenAPI 仕様から推論される
throw new ApiError(error);
}
// data.users — User[] 型が推論される
return data;
}この方法の最大の利点は、OpenAPI 仕様を更新して型を再生成するだけで、バックエンドとフロントエンドの型が自動的に同期されることです。
Antigravity エージェントによるテスト自動生成
API テストの自動生成
Antigravity のエージェントに OpenAPI 仕様を読み込ませ、テストコードを自動生成する手法です。
# Antigravity プロンプト例
openapi.yaml の全エンドポイントに対して、
以下の観点でテストケースを Vitest で生成してください:
1. 正常系(200/201 レスポンス)
2. バリデーションエラー(400)
3. 認証エラー(401)
4. 権限エラー(403)
5. 存在しないリソース(404)
6. レート制限(429)
各テストには明確な説明文とアサーションを含めてください。生成されるテストコードの例です。
// tests/api/users.test.ts
import { describe, it, expect, beforeAll } from "vitest";
import { createTestClient } from "../helpers/test-client";
describe("GET /users", () => {
const client = createTestClient();
it("ページネーション付きでユーザー一覧を取得できる", async () => {
const res = await client.get("/users?page=1&limit=10");
expect(res.status).toBe(200);
expect(res.body.users).toBeInstanceOf(Array);
expect(res.body.users.length).toBeLessThanOrEqual(10);
expect(res.body.total).toBeTypeOf("number");
expect(res.body.page).toBe(1);
});
it("limit の上限を超えると 400 を返す", async () => {
const res = await client.get("/users?limit=999");
expect(res.status).toBe(400);
expect(res.body.type).toContain("validation-error");
// RFC 7807 形式のエラーレスポンスを検証
expect(res.body).toHaveProperty("title");
expect(res.body).toHaveProperty("detail");
});
it("認証なしでアクセスすると 401 を返す", async () => {
const unauthClient = createTestClient({ auth: false });
const res = await unauthClient.get("/users");
expect(res.status).toBe(401);
});
});
describe("POST /users", () => {
const adminClient = createTestClient({ role: "admin" });
it("管理者がユーザーを作成できる", async () => {
const res = await adminClient.post("/users", {
body: {
email: "new@example.com",
name: "New User",
},
});
expect(res.status).toBe(201);
expect(res.body.id).toBeDefined();
expect(res.body.email).toBe("new@example.com");
});
it("一般ユーザーは作成権限がなく 403 を返す", async () => {
const userClient = createTestClient({ role: "user" });
const res = await userClient.post("/users", {
body: { email: "test@example.com", name: "Test" },
});
expect(res.status).toBe(403);
});
});コントラクトテスト — 仕様と実装の乖離を検出
API ファースト開発で最も重要なのは、仕様と実装が常に一致していることを保証するコントラクトテストです。
// tests/contract/openapi-contract.test.ts
import { describe, it, expect } from "vitest";
import SwaggerParser from "@apidevtools/swagger-parser";
import Ajv from "ajv";
import addFormats from "ajv-formats";
import { createTestClient } from "../helpers/test-client";
describe("OpenAPI Contract Tests", () => {
let api: any;
let ajv: Ajv;
beforeAll(async () => {
// OpenAPI 仕様をパース
api = await SwaggerParser.dereference("openapi.yaml");
ajv = new Ajv({ allErrors: true, strict: false });
addFormats(ajv);
});
it("GET /users のレスポンスが仕様と一致する", async () => {
const client = createTestClient();
const res = await client.get("/users");
// 仕様からスキーマを取得
const schema =
api.paths["/users"].get.responses["200"]
.content["application/json"].schema;
// レスポンスが仕様に準拠しているか検証
const validate = ajv.compile(schema);
const valid = validate(res.body);
if (!valid) {
console.error("Schema violations:", validate.errors);
}
expect(valid).toBe(true);
});
});このコントラクトテストを CI に組み込むことで、「仕様書を更新したが実装を忘れた」「実装を変更したが仕様書を更新しなかった」というヒューマンエラーを自動的に検出できます。
API ドキュメントの自動生成と公開
Redoc / Swagger UI の統合
OpenAPI 仕様から美しい API ドキュメントを自動生成し、デプロイする方法です。
// src/app/api/docs/route.ts — Next.js API Route でドキュメントを配信
import { readFileSync } from "fs";
import path from "path";
const specYaml = readFileSync(
path.join(process.cwd(), "openapi.yaml"),
"utf-8"
);
export async function GET() {
const html = `<!DOCTYPE html>
<html>
<head>
<title>API Documentation</title>
<meta charset="utf-8" />
<link href="https://fonts.googleapis.com/css?family=Inter:300,400,600" rel="stylesheet">
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>
</head>
<body>
<div id="redoc-container"></div>
<script>
Redoc.init('/api/docs/spec', {
theme: {
colors: { primary: { main: '#1a73e8' } },
typography: { fontFamily: 'Inter, sans-serif' },
},
hideDownloadButton: false,
expandResponses: '200,201',
}, document.getElementById('redoc-container'));
</script>
</body>
</html>`;
return new Response(html, {
headers: { "Content-Type": "text/html" },
});
}Antigravity エージェントによるドキュメント品質向上
仕様書のドキュメント品質を向上させるためのプロンプトパターンです。
# Antigravity プロンプト例
openapi.yaml の以下の点を改善してください:
1. 全エンドポイントに description を追加(ユースケースを含める)
2. 全パラメータに example 値を追加
3. レスポンスに実用的な example を追加
4. tags でエンドポイントをグループ化
5. セキュリティスキームの説明を充実させるCI/CD パイプラインへの統合
GitHub Actions による自動検証パイプライン
OpenAPI 仕様の変更を起点に、型生成・テスト・ドキュメント生成を自動実行するパイプラインです。
# .github/workflows/api-first.yaml
name: API First Pipeline
on:
push:
paths:
- "openapi.yaml"
- "src/**"
pull_request:
paths:
- "openapi.yaml"
jobs:
validate-and-generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
- run: npm ci
# 1. OpenAPI 仕様のバリデーション
- name: Validate OpenAPI spec
run: npx @redocly/cli lint openapi.yaml
# 2. 型定義の再生成
- name: Generate TypeScript types
run: npx openapi-typescript openapi.yaml -o src/generated/api-types.ts
# 3. Zod スキーマの再生成
- name: Generate Zod schemas
run: npx openapi-zod-client openapi.yaml -o src/generated/api-schemas.ts
# 4. 生成コードに差分がないか確認
- name: Check for uncommitted generated files
run: |
git diff --exit-code src/generated/ || {
echo "❌ Generated files are out of date!"
echo "Run: npm run generate:api and commit the changes"
exit 1
}
# 5. 型チェック
- name: TypeScript type check
run: npx tsc --noEmit
# 6. コントラクトテスト
- name: Run contract tests
run: npx vitest run tests/contract/
# 7. API テスト
- name: Run API tests
run: npx vitest run tests/api/Breaking Change 検出
API の後方互換性を破壊する変更を PR の段階で検出する仕組みです。
# GitHub Actions の追加ステップ
- name: Detect breaking changes
if: github.event_name == 'pull_request'
run: |
# メインブランチの仕様を取得
git fetch origin main
git show origin/main:openapi.yaml > /tmp/openapi-base.yaml
# 差分を検出
npx @redocly/cli diff /tmp/openapi-base.yaml openapi.yaml \
--fail-on-incompatibleこの設定により、「レスポンスの必須フィールドを削除した」「エンドポイントの URL を変更した」といった互換性を破壊する変更が PR のチェックで自動的にブロックされます。
実践的な設計パターン集
パターン 1: バージョニング戦略
# URL パスベースのバージョニング
servers:
- url: "https://api.example.com/v1"
- url: "https://api.example.com/v2"
# ヘッダーベースのバージョニング
paths:
/users:
get:
parameters:
- name: API-Version
in: header
schema:
type: string
enum: ["2026-01-01", "2026-04-01"]
default: "2026-04-01"パターン 2: エラーレスポンスの標準化(RFC 7807)
components:
schemas:
ProblemDetail:
type: object
required: [type, title, status]
properties:
type:
type: string
format: uri
description: "エラータイプを識別する URI"
# 例: "https://api.example.com/errors/validation"
title:
type: string
description: "人間が読めるエラータイトル"
status:
type: integer
description: "HTTP ステータスコード"
detail:
type: string
description: "エラーの詳細な説明"
instance:
type: string
format: uri
description: "このエラー発生のインスタンスURI"
errors:
type: array
items:
type: object
properties:
field:
type: string
message:
type: stringパターン 3: カーソルベースページネーション
# components/parameters で再利用可能に定義
components:
parameters:
CursorParam:
name: cursor
in: query
description: "次のページの起点となるカーソル値"
schema:
type: string
LimitParam:
name: limit
in: query
schema:
type: integer
default: 20
minimum: 1
maximum: 100
schemas:
PaginationMeta:
type: object
properties:
hasNext:
type: boolean
nextCursor:
type: string
nullable: true
total:
type: integerまとめ
API ファースト開発は、最初に仕様を定義する一手間がかかるように見えますが、開発が進むにつれてその投資は何倍ものリターンを生みます。Antigravity の AI エージェントを活用することで、仕様書の作成・型生成・テスト自動生成・ドキュメント公開のすべてを高速に回せるようになり、「仕様と実装の乖離」という開発チームの永遠の課題を根本から解消できます。
まずは小さなプロジェクトで openapi.yaml を1ファイル作成し、型生成とコントラクトテストの仕組みを導入してみてください。その効果を実感したら、既存プロジェクトへの段階的な導入に進みましょう。
セキュリティや認証パターンのさらに詳しい実装については Antigravity エージェントでセキュアな API バックエンドを構築する — 認証・バリデーション・レート制限の実装パターン も参照してみてください。また、GraphQL との比較に興味のある方は Antigravity × GraphQL + Apollo Server 実践チュートリアル もぜひご覧ください。