API仕様書をExcelやWordで手書きしていて、実装との乖離が発生している——こうした課題を解決するのがOpenAPI仕様です。API定義を機械可読なフォーマットで記述し、仕様書の自動生成、クライアントコードの自動生成、バリデーションまでを自動化できます。
本記事では、OpenAPI仕様の基本的な書き方からSwagger UIの活用方法、コード生成ツール、CI/CDへの統合まで実践的に解説します。
OpenAPI仕様とは何か
OpenAPI Specification(OAS)は、REST APIのインターフェースを記述するための標準フォーマットです。もともとSwaggerと呼ばれていたプロジェクトが、2015年にLinux Foundation傘下のOpenAPI Initiativeに移管され、現在の名称になりました。
OpenAPIがもたらす価値
仕様書の自動生成
YAMLまたはJSON形式でAPI定義を書くだけで、Swagger UIにより美しいインタラクティブなAPIドキュメントが自動生成されます。手書きの仕様書と異なり、常に最新の状態を保てます。
コード生成
API定義からサーバースタブやクライアントライブラリを自動生成できます。TypeScript、Java、Python、Go、C#など、多くの言語に対応しています。
バリデーション
リクエスト・レスポンスがAPI定義に準拠しているかを自動的に検証できます。実装のバグを早期に発見できます。
テストの自動化
API定義からテストケースを自動生成し、APIの動作を自動検証できます。
API設計ファースト
コードを書く前にAPIの設計をOpenAPI仕様で定義し、フロントエンドとバックエンドの開発を並行して進められます。
Swagger UIとSwagger Editorの違い
Swagger Editor
ブラウザ上でOpenAPI定義ファイルを編集し、リアルタイムにプレビューできるエディタです。editor.swagger.ioで無料で利用できます。
Swagger UI
OpenAPI定義ファイルからインタラクティブなAPIドキュメントを生成するツールです。実際にAPIを呼び出すTry it out機能も備えています。
OpenAPI定義ファイルの基本構造
OpenAPI 3.0の定義ファイルの基本構造を見ていきましょう。
最小限の定義
# openapi.yaml
openapi: "3.0.3" # OpenAPIのバージョン
info:
title: ユーザー管理API
description: ユーザーのCRUD操作を提供するREST API
version: "1.0.0"
contact:
name: API サポート
email: api-support@example.com
servers:
- url: https://api.example.com/v1
description: 本番環境
- url: https://staging-api.example.com/v1
description: ステージング環境
- url: http://localhost:3000/v1
description: ローカル開発環境
paths:
/users:
get:
summary: ユーザー一覧を取得
operationId: getUsers
tags:
- Users
parameters:
- name: page
in: query
description: ページ番号
schema:
type: integer
default: 1
minimum: 1
- name: limit
in: query
description: 1ページあたりの件数
schema:
type: integer
default: 20
minimum: 1
maximum: 100
responses:
"200":
description: ユーザー一覧の取得に成功
content:
application/json:
schema:
$ref: "#/components/schemas/UserListResponse"
"401":
$ref: "#/components/responses/Unauthorized"
post:
summary: ユーザーを作成
operationId: createUser
tags:
- Users
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateUserRequest"
example:
name: "田中太郎"
email: "tanaka@example.com"
age: 30
responses:
"201":
description: ユーザーの作成に成功
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
$ref: "#/components/responses/BadRequest"
"409":
description: メールアドレスが既に登録されている
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/users/{userId}:
get:
summary: ユーザーの詳細を取得
operationId: getUserById
tags:
- Users
parameters:
- name: userId
in: path
required: true
description: ユーザーID
schema:
type: string
format: uuid
responses:
"200":
description: ユーザー詳細の取得に成功
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"404":
$ref: "#/components/responses/NotFound"
スキーマ定義(components/schemas)
components:
schemas:
User:
type: object
required:
- id
- name
- email
- createdAt
properties:
id:
type: string
format: uuid
description: ユーザーの一意識別子
example: "550e8400-e29b-41d4-a716-446655440000"
name:
type: string
description: ユーザー名
minLength: 1
maxLength: 100
example: "田中太郎"
email:
type: string
format: email
description: メールアドレス
example: "tanaka@example.com"
age:
type: integer
minimum: 0
maximum: 150
description: 年齢
example: 30
role:
type: string
enum:
- admin
- member
- guest
default: member
description: ユーザーの権限
createdAt:
type: string
format: date-time
description: 作成日時
example: "2026-03-27T10:00:00Z"
CreateUserRequest:
type: object
required:
- name
- email
properties:
name:
type: string
minLength: 1
maxLength: 100
email:
type: string
format: email
age:
type: integer
minimum: 0
maximum: 150
UserListResponse:
type: object
properties:
data:
type: array
items:
$ref: "#/components/schemas/User"
pagination:
$ref: "#/components/schemas/Pagination"
Pagination:
type: object
properties:
currentPage:
type: integer
example: 1
totalPages:
type: integer
example: 10
totalCount:
type: integer
example: 195
limit:
type: integer
example: 20
Error:
type: object
required:
- code
- message
properties:
code:
type: string
description: エラーコード
example: "VALIDATION_ERROR"
message:
type: string
description: エラーメッセージ
example: "入力値が不正です"
details:
type: array
items:
type: object
properties:
field:
type: string
message:
type: string
responses:
BadRequest:
description: リクエストが不正
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
Unauthorized:
description: 認証が必要
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
NotFound:
description: リソースが見つからない
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
security:
- BearerAuth: []
Swagger UIでAPIドキュメントを公開する
OpenAPI定義ファイルからSwagger UIでインタラクティブなドキュメントを公開する方法を紹介します。
Express.jsに組み込む方法
// swagger-uiをExpressアプリに組み込む
import express from 'express';
import swaggerUi from 'swagger-ui-express';
import YAML from 'yamljs';
const app = express();
// OpenAPI定義ファイルを読み込み
const swaggerDocument = YAML.load('./openapi.yaml');
// Swagger UIのセットアップ
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, {
customCss: '.swagger-ui .topbar { display: none }',
customSiteTitle: 'ユーザー管理API ドキュメント',
swaggerOptions: {
persistAuthorization: true, // 認証情報を保持
tryItOutEnabled: true, // Try it outをデフォルトで有効
},
}));
// APIのルート定義
app.use('/v1', apiRoutes);
app.listen(3000, () => {
console.log('サーバー起動: http://localhost:3000');
console.log('APIドキュメント: http://localhost:3000/api-docs');
});
Docker Composeで独立した環境を構築
# docker-compose.yml
version: '3.8'
services:
swagger-ui:
image: swaggerapi/swagger-ui
ports:
- "8080:8080"
volumes:
- ./openapi.yaml:/usr/share/nginx/html/openapi.yaml
environment:
- SWAGGER_JSON=/usr/share/nginx/html/openapi.yaml
- BASE_URL=/api-docs
swagger-editor:
image: swaggerapi/swagger-editor
ports:
- "8081:8080"
上記の設定で、http://localhost:8080でSwagger UIを、http://localhost:8081でSwagger Editorをそれぞれ利用できます。
コード自動生成の活用
OpenAPI定義からクライアントコードやサーバースタブを自動生成する方法を紹介します。
TypeScriptクライアントの生成
# openapi-generatorのインストール
npm install -g @openapitools/openapi-generator-cli
# TypeScriptクライアントの生成
openapi-generator-cli generate \
-i openapi.yaml \
-g typescript-fetch \
-o ./generated/client \
--additional-properties=supportsES6=true,typescriptThreePlus=true
# 別のツール: openapi-typescript(型定義のみ生成)
npm install -D openapi-typescript
npx openapi-typescript openapi.yaml -o ./generated/api-types.ts
生成されたコードの使用例を見てみましょう。
// 自動生成されたクライアントの使用例
import { UsersApi, Configuration } from './generated/client';
const config = new Configuration({
basePath: 'https://api.example.com/v1',
headers: {
'Authorization': 'Bearer eyJhbGci...',
},
});
const usersApi = new UsersApi(config);
// 型安全なAPI呼び出し
async function fetchUsers() {
// getUsers の引数と戻り値が型付けされている
const response = await usersApi.getUsers({
page: 1,
limit: 20,
});
// response.data の型が UserListResponse として推論される
for (const user of response.data) {
console.log(user.name); // 補完が効く
console.log(user.email); // 型チェックされる
}
}
openapi-typescriptとopenapi-fetchの組み合わせ
より軽量なアプローチとして、型定義だけを生成して既存のfetchクライアントと組み合わせる方法もあります。
# 型定義の生成
npm install -D openapi-typescript
npm install openapi-fetch
npx openapi-typescript openapi.yaml -o ./src/api/schema.d.ts
// src/api/client.ts
import createClient from 'openapi-fetch';
import type { paths } from './schema';
const client = createClient<paths>({
baseUrl: 'https://api.example.com/v1',
});
// 型安全なAPI呼び出し
async function getUser(userId: string) {
const { data, error } = await client.GET('/users/{userId}', {
params: {
path: { userId },
},
});
if (error) {
// error の型が自動的に推論される
console.error(error.message);
return;
}
// data の型が User として推論される
console.log(data.name);
}
リクエスト・レスポンスのバリデーション
OpenAPI定義を使って、実際のAPIリクエストとレスポンスを自動的に検証できます。
express-openapi-validatorの導入
// express-openapi-validatorを使ったバリデーション
import express from 'express';
import * as OpenApiValidator from 'express-openapi-validator';
const app = express();
app.use(express.json());
// OpenAPIバリデーションミドルウェアの設定
app.use(
OpenApiValidator.middleware({
apiSpec: './openapi.yaml',
validateRequests: true, // リクエストのバリデーション
validateResponses: true, // レスポンスのバリデーション(開発環境のみ推奨)
validateSecurity: {
handlers: {
BearerAuth: (req, scopes, schema) => {
// JWTトークンの検証ロジック
const token = req.headers.authorization?.split(' ')[1];
if (!token) return false;
// トークンの検証...
return true;
},
},
},
})
);
// ルート定義
app.post('/v1/users', (req, res) => {
// req.body は OpenAPI定義に基づいてバリデーション済み
// name、emailが必須で、ageが0-150の範囲であることが保証されている
const user = createUser(req.body);
res.status(201).json(user);
});
// バリデーションエラーのハンドリング
app.use((err, req, res, next) => {
if (err.status === 400) {
res.status(400).json({
code: 'VALIDATION_ERROR',
message: 'リクエストが不正です',
details: err.errors.map(e => ({
field: e.path,
message: e.message,
})),
});
} else {
next(err);
}
});
バリデーションを導入することで、APIコントローラー内でのバリデーションコードを大幅に削減できます。OpenAPI定義が「信頼できる唯一の情報源(Single Source of Truth)」として機能します。
CI/CDパイプラインへの統合
OpenAPI定義をCI/CDパイプラインに組み込むことで、API品質を継続的に維持できます。
定義ファイルのリント・バリデーション
# Spectral を使ったOpenAPI定義のリント
npm install -D @stoplight/spectral-cli
# リントの実行
npx spectral lint openapi.yaml
# .spectral.yml - カスタムルールの定義
extends:
- spectral:oas
rules:
# operationIdの必須化
operation-operationId: error
# レスポンスにdescriptionを必須化
oas3-valid-media-example: warn
# カスタムルール: パス名はケバブケースを強制
path-kebab-case:
description: "パス名はケバブケースで記述すること"
severity: error
given: "$.paths[*]~"
then:
function: pattern
functionOptions:
match: "^(/[a-z0-9-]+|/{[a-zA-Z]+})+$"
GitHub Actionsでの自動チェック
# .github/workflows/openapi.yml
name: OpenAPI Validation
on:
pull_request:
paths:
- 'openapi.yaml'
- 'openapi/**'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: OpenAPI定義のバリデーション
run: |
npm install -g @stoplight/spectral-cli
spectral lint openapi.yaml
- name: 破壊的変更の検出
run: |
npm install -g oasdiff
# mainブランチの定義と比較して破壊的変更を検出
git fetch origin main
git show origin/main:openapi.yaml > openapi-base.yaml
oasdiff breaking openapi-base.yaml openapi.yaml
- name: TypeScript型定義の再生成
run: |
npx openapi-typescript openapi.yaml -o ./src/api/schema.d.ts
git diff --exit-code src/api/schema.d.ts || {
echo "型定義が更新されています。再生成してコミットしてください。"
exit 1
}
破壊的変更の自動検出は特に重要です。エンドポイントの削除、必須パラメータの追加、レスポンスフィールドの削除などを自動的に検出し、意図しない互換性破壊を防ぎます。
OpenAPI定義の設計ベストプラクティス
実務でOpenAPI定義を書く際のベストプラクティスをまとめます。
スキーマ設計のポイント
$refを活用して再利用性を高める
共通のスキーマはcomponents/schemasに定義し、$refで参照します。定義の重複を避け、変更時の影響を最小化します。
exampleを充実させる
各フィールドやリクエスト・レスポンスにexampleを記載することで、Swagger UIでの理解がしやすくなります。
バリデーションルールを明記する
minLength、maxLength、minimum、maximum、pattern(正規表現)、enumなどを使って、フィールドの制約を明確にします。
エラーレスポンスを統一する
エラーレスポンスの形式を全エンドポイントで統一し、共通のErrorスキーマを$refで参照します。
ファイル分割の方法
大規模なAPIでは、1つのファイルに全定義を書くと管理が困難になります。ファイルを分割しましょう。
# ディレクトリ構成の例
openapi/
├── openapi.yaml # メインファイル($refで他ファイルを参照)
├── paths/
│ ├── users.yaml # /users エンドポイントの定義
│ ├── posts.yaml # /posts エンドポイントの定義
│ └── comments.yaml
├── schemas/
│ ├── User.yaml
│ ├── Post.yaml
│ └── Error.yaml
└── parameters/
└── common.yaml # 共通パラメータ
# openapi.yaml(メインファイル)からの参照
paths:
/users:
$ref: './paths/users.yaml#/users'
/users/{userId}:
$ref: './paths/users.yaml#/users~1{userId}'
components:
schemas:
User:
$ref: './schemas/User.yaml'
まとめ
OpenAPI仕様は、API開発の品質と効率を大幅に向上させる強力なツールです。本記事のポイントを振り返りましょう。
仕様駆動開発
OpenAPI定義をAPI設計の起点とし、コード実装前にフロントエンド・バックエンド間でAPIの合意を取ることで、手戻りを大幅に削減できます。
Swagger UIの活用
インタラクティブなAPIドキュメントにより、APIの理解と動作確認が容易になります。
コード生成の恩恵
TypeScriptクライアントの自動生成により、型安全なAPI呼び出しを実現し、実装ミスを防ぎます。
バリデーションの自動化
express-openapi-validatorなどを使って、リクエスト・レスポンスの検証を自動化し、手動バリデーションコードを削減します。
CI/CDとの統合
リント、破壊的変更検出、型定義の自動更新をパイプラインに組み込み、API品質を継続的に維持します。
まずは既存のAPIに対してOpenAPI定義ファイルを書き、Swagger UIで可視化してみることをおすすめします。仕様書の自動生成だけでも十分な価値がありますし、そこからコード生成やバリデーションへと段階的に活用範囲を広げていけます。
関連記事
AIエージェント開発入門|自律型AIの仕組みと構築方法を解説【2026年版】
AI駆動コーディングワークフロー|Claude Code・Cursor・Copilotの実践的使い分け
プロンプトエンジニアリング上級編|Chain-of-Thought・Few-Shot・ReActの実践
APIレート制限の設計と実装|トークンバケット・スライディングウィンドウ解説
APIバージョニング戦略|URL・ヘッダー・クエリパラメータの使い分け
BIツール入門|Metabase・Redash・Looker Studioでデータ可視化する方法
チャットボット開発入門|LINE Bot・Slack Botの構築方法と活用事例
CI/CDパイプラインの基礎|継続的インテグレーション・デリバリーの全体像