こんにちは!日々のAPI開発、本当にお疲れ様です。
開発初期は「よし、今日もバリバリAPIを書くぞ!」と意気込んでいたものの、数ヶ月が経ち、エンドポイントが100個、200個と増えていくうちに、ひとつの巨大な `openapi.yaml` が誕生してしまう……。そして、スクロールしてもスクロールしても終わらない地獄の縦長ファイルを前に、「どこを直せばいいんだっけ?」と途方に暮れる。そんな経験、ありませんか?
今回は、その巨大化してモンスター化したSwagger(OpenAPI)定義書を美しく解体し、「見通しが良くて、誰もが迷わないモジュール設計」へと生まれ変わらせる技術を伝授します。
主役は、OpenAPIの秘孔とも言うべき `$ref`(参照)機能 です。これをマスターすれば、毎日のAPI設計・ドキュメント確認の作業が劇的に楽になりますよ。さあ、一緒にスマートなAPI設計の世界へ足を踏み入れましょう!
—
1. なぜ巨大なSwagger定義書は「悪」なのか?
APIファーストで開発を進めるチームにとって、OpenAPI定義書は「設計図」であり「契約書」です。しかし、これが数千行の単一ファイルになると、以下のような致命的な問題が発生します。
- コンフリクトの多発: 複数の開発者が同時に別々のAPIを追加・修正しようとすると、Gitのマージで必ずと言っていいほどコンフリクトが起きます。
- 認知負荷の増大: 「ユーザー作成API」を直したいだけなのに、「商品データ」「決済データ」「エラーレスポンス」の巨大なスキーマの海を泳がされることになります。
- 再利用性の欠如: 同じようなエラーレスポンスの定義やページネーションのクエリパラメータを、あちこちにコピペする羽目になります。
これを解決するのが、「関心の分離」に基づいたファイルの分割と `$ref` による結合 です。
—
2. `$ref` の基本:外部ファイルを参照する魔法
`$ref`(Reference)は、JSON Pointer(RFC 6901)の仕様をベースにしており、定義書の特定の部分を別の場所(あるいは別のファイル)から「ここにあるものを使ってね」と指し示すための機能です。
まずは、最もシンプルなローカルファイル分割のディレクトリ構成から見ていきましょう。
推奨するディレクトリ構成
api/
├── openapi.yaml # エントリポイント(全体の骨組みだけを書く)
├── paths/
│ ├── users.yaml # ユーザー関連のエンドポイント
│ └── products.yaml # 商品関連のエンドポイント
└── components/
├── schemas/
│ ├── user.yaml # ユーザーオブジェクトのスキーマ
│ └── error.yaml # エラーレスポンスのスキーマ
└── parameters.yaml # 共通パラメータ(ページネーションなど)
どうですか? この構成を見るだけで、「どこに何があるか」が一目瞭然ですよね。
—
3. 実践!ファイルを美しく分割してみよう
それでは実際に、単一ファイルだった定義書を分割してみましょう。ここでは「ユーザー情報取得API」を例にとります。
① エントリポイント (`api/openapi.yaml`)
まず、全体の根幹となるファイルです。ここでは詳細なスキーマは書かず、各パーツへの「ポインタ」だけを記述します。
openapi: 3.0.3
info:
title: 魂のECサイト API
version: 1.0.0
すべてのパス(エンドポイント)は別ファイルから読み込む
paths:
/users:
$ref: ‘./paths/users.yaml’
すべての共通部品は別ファイルから読み込む
components:
schemas:
User:
$ref: ‘./components/schemas/user.yaml’
Error:
$ref: ‘./components/schemas/error.yaml’
先輩のワンポイントアドバイス:
エントリポイントには「全体のメタデータ」と「大まかなルーティングのルーティング先」だけを書くのが、美しさを保ち続ける秘訣です。
② パス定義ファイル (`api/paths/users.yaml`)
次に、 `/users` エンドポイントの詳細を定義します。パス単体を記述する際は、ルートに `get` や `post` などのHTTPメソッドを直接配置します。
get:
summary: ユーザー一覧取得
description: 登録されているユーザーの一覧を返します。
responses:
‘200’:
description: 成功
content:
application/json:
schema:
type: array
items:
# components/schemas/user.yaml を参照する
$ref: ‘../components/schemas/user.yaml’
‘400’:
description: 不正なリクエスト
content:
application/json:
schema:
# 共通のエラー定義を参照する
$ref: ‘../components/schemas/error.yaml’
ここで重要なのは、パスファイルから見た相対パスで `$ref` のリンク先を指定することです。階層が深くなると `../` が増えるので、ディレクトリ階層は最大でも2〜3階層までに抑えるのがスマートです。
③ スキーマ定義ファイル (`api/components/schemas/user.yaml`)
最後に、データ構造(オブジェクト)の定義です。純粋なJSON Schemaそのものを記述します。
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
example: 1
name:
type: string
example: “山田 太郎”
email:
type: string
format: email
example: “yamada@example.com”
これだけで、データ構造の変更があった場合も `user.yaml` を修正するだけで、それを参照しているすべてのエンドポイントに自動的に変更が反映されます。コピペミスや修正漏れとは、今日でお別れです!
—
4. 開発時の注意点とバンドルツールの活用
ファイルを分割すると、「Swagger UIなどのツールが、複数のファイルを正しく読み込めるか?」という疑問が湧いてきますよね。
実は、Swagger UIや一部のモックサーバーは、ブラウザのセキュリティ制限(CORS)やファイル読み込みの仕組み上、複数のローカルファイルを動的に `$ref` で読み込むのが苦手な場合があります。
そこで登場するのが、バンドルツール(Bundle Tool)です。
圧倒的におすすめ:`Redocly CLI`
現在、OpenAPIのlint(構文チェック)やバンドルにおいてデファクトスタンダードになりつつあるのが Redocly CLI です。これを使えば、分割した複数のファイルを、一瞬で「美しく完璧な1枚の巨大YAMLファイル」に合体(Bundle)させることができます。
インストール:
npm install -g @redocly/cli
バンドルコマンドの実行:
redocly bundle api/openapi.yaml -o dist/bundle.yaml
これだけで、`api/` 以下に散らばっていたすべてのモジュールが解決され、`dist/bundle.yaml` という1つの完璧なファイルが出力されます。CI/CDパイプラインにこのコマンドを組み込んでおけば、常に最新の結合済み定義書を自動生成してSwagger UIやAPIゲートウェイに渡すことができます。
また、以下のコマンドでリアルタイムに構文エラーをチェックすることも可能です。
redocly lint api/openapi.yaml
「`$ref` のパスが間違っているよ!」といったミスも事前に教えてくれるため、開発の質が跳ね上がります。
—
まとめ:モジュール設計でAPI開発を加速させよう
今回は、Swagger(OpenAPI)の `$ref` を用いたモジュール設計の極意について解説しました。
- 巨大な定義書はチーム開発のボトルネックになるため、早めに分割する。
- エントリポイント、パス、スキーマ(components)でディレクトリを綺麗に整理する。
- ローカルでの分割管理をしつつ、表示や配布の際は `Redocly CLI` などのバンドルツールで1枚にまとめる。
この設計手法を取り入れるだけで、API設計の見通しが良くなり、チームメンバーからの「ここ、どうなってるんだっけ?」という質問が劇的に減ります。
明日からのAPI開発、ぜひあなたのプロジェクトでも試してみてください。あなたのエンジニアリングライフが、もっと快適でエキサイティングなものになりますように!