Web開発をしていると、APIエンドポイントの命名は当たり前のように /api/users や /api/v1/auth という形をとります。私自身もこれまで複数のプロジェクトでこの命名規則に従ってきましたが、以前メンターの方に「なぜ /api をつけるのか?」という疑問が投げかけられました。
最初は単なる慣習だと思っていましたが、Dockerなどのインフラ周りを学んでいくうちに、その背景にある技術的な合理性が見えてきたので、自分なりの考察をまとめます。
大前提: そもそもURLとは何か
この疑問を深掘りする前に、URLの本来の意味を確認しておきます。
URLは Uniform Resource Locator の略称です。直訳すると「リソースの場所を統一的に示すもの」。つまりURLとは、ネットワーク上のあるリソース(サービス・データ・ファイルなど)がどこに存在するのかを示す住所のようなものです。
http://localhost:8080/api/users
│ │ │ │ └─ リソースの種類
│ │ │ └────── サービスの識別(後述)
│ │ └─────────── ポート(どの入口か)
│ └─────────────────── ホスト(どのマシンか)
└──────────────────────────── プロトコル(通信方式)
この「リソースの場所を示す」という本質を念頭に置くと、URLの各パートには必然的に意味があるはずだと考えました。/api というパスも、単なる飾りではなく、リソースの所在を示す重要な一部なのではないかと。
この視点を持ったうえで、Dockerのコンテナ構成を見ていきます。
/api プレフィックスの役割
疑問: そもそもなぜ /api をつけるのか
ローカル開発ではエンドポイントを以下のように定義することが多いと思います。
GET /api/users
POST /api/auth/login
私はこれまで /api をつけることが多い一方で、個人開発等でAIにコードを生成させると /api/v1/users のようにバージョン番号までつけてくることがよくありました。この違いが気になりすぎて、そもそもの理由を考えるようになりました。
考察: Dockerコンテナ構成とURLの関係
プロジェクトが大きくなると、Docker Composeで起動するコンテナは「フロントエンド」「バックエンド」という単純な2分割ではなくなります。外部サービスの機能ごとにコンテナを分けるケースが増えてきます。
例えば、以下のような構成です。
| コンテナ名 | 役割 |
|---|---|
api |
アプリケーションのAPIサーバー |
rdb |
リレーショナルデータベース |
rustfs |
オブジェクトストレージ(S3互換) |
これらのコンテナは独立して起動されるため、お互いの場所(ネットワーク上のアドレス)をあらかじめ知っているわけではありません。Docker Composeのネットワーク内ではコンテナ名がホスト名として機能するため、例えば api コンテナから見た rustfs のURLは以下のようになります。
http://rustfs:9000
一方、ホストマシン(例えばローカルで起動したフロントエンド)から同じサービスにアクセスする場合は次のようになります。
http://localhost:9000
このように、同じサービスでも「誰から見るか」によってURLが変わるのがDockerネットワークの特徴です。URLはコンテナ同士をつなぐ重要な役割を果たしており、この経験から「/api というパスにも、ルーティング上の明確な意味があるのではないか」と考えるようになりました。
実際の主な理由:リバースプロキシによるルーティング
ここからは、上記の考察を踏まえつつ、より一般的に知られている理由を整理します。
/api プレフィックスの最大の理由は、リバースプロキシ(Nginx等)でのルーティングです。
リバースプロキシとは、クライアントとバックエンドサーバーの間に立ち、クライアントからのリクエストを受け取って適切なサーバーに転送する中継役のことです。クライアントはリバースプロキシとだけ通信し、背後にどんなサーバーが何台あるかを意識する必要がありません。
クライアント → [リバースプロキシ (Nginx)] → /api/* → apiコンテナ
→ /* → フロントエンド
本番環境やDocker環境では、フロントエンドとバックエンドを同一ドメインでホストし、NginxなどのリバースプロキシがURLパスを見てリクエストを振り分けるのが一般的です。
server {
listen 80;
# /api で始まるリクエスト → バックエンド(apiコンテナ)に転送
location /api/ {
proxy_pass http://api:8080/;
}
# それ以外 → フロントエンドの静的ファイルを返す
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
}
つまり、/api というプレフィックスがあることで、リバースプロキシが「このリクエストはAPIサーバーに転送すべきだ」と判断できるわけです。
これは、私がDockerのコンテナ構成から感じた「コンテナを識別するための名前」という考察とも方向性としては近く、パスプレフィックスがサービスの振り分けに使われているという点では本質的に同じです。
その他の理由
/api プレフィックスには、ルーティング以外にもいくつかの実用的な理由があります。
-
名前空間の分離:UIのルーティング(
/users,/dashboard)とAPIのルーティングが衝突しないようにする -
CORS設定の簡素化:
/api以下をまとめてCORS設定の対象にできる -
セキュリティポリシーの適用:認証・レート制限などを
/api配下に一括で適用しやすい - 可読性・開発体験:URLを見ただけで「これはAPI呼び出しだ」とわかる
/v1 バージョニングの役割
なぜバージョンをURLに含めるのか
/api/v1/users のように v1 をつけるのは、APIバージョニングのためです。
APIは一度公開すると、すでにそのAPIを利用しているクライアント(モバイルアプリ、外部サービスなど)が存在します。レスポンスの構造を変更したり、既存のフィールドを削除したりすると、それらのクライアントが壊れてしまいます。
# 旧バージョン(既存クライアントはこちらを使い続ける)
GET /api/v1/users → { "name": "田中太郎" }
# 新バージョン(新しいクライアント向け)
GET /api/v2/users → { "first_name": "太郎", "last_name": "田中" }
このように、URLにバージョンを含めることで破壊的な変更も安全に導入できます。
v1は常に必要か?
結論から言うと、プロジェクトの規模と性質によります。
| ケース | 推奨 |
|---|---|
| 社内ツール・個人開発 |
/api/users でOK。バージョニングは必要になったときに導入すれば十分 |
| 外部公開API(SaaS等) |
/api/v1/users を最初から導入すべき。後から追加するのは困難 |
| モバイルアプリのバックエンド | バージョニング推奨。旧バージョンのアプリが残り続けるため |
なお、URLにバージョンを含める方式(URLバージョニング)以外にも、ヘッダーベースのバージョニングという手法もあります。
# ヘッダーベースの例
GET /api/users
Accept: application/vnd.myapp.v2+json
ただし、URLバージョニングの方が直感的でデバッグしやすいため、広く採用されています。
まとめ
| 要素 | 主な理由 |
|---|---|
/api |
リバースプロキシによるルーティング、名前空間の分離、セキュリティポリシーの一括適用 |
/v1 |
APIバージョニング。破壊的変更から既存クライアントを保護する |
Dockerのコンテナ構成を学ぶ中で「URLはサービス同士をつなぐ重要な識別子である」ということに気づき、そこからAPIエンドポイントの命名にも合理的な背景があることが見えてきました。単なる慣習ではなく、インフラ設計やAPI設計のベストプラクティスに根ざした命名であることを理解しておくと、設計時の判断がより確信を持てるようになると思います。
他にも実践的な観点での理由があればコメントでご享受いただけると幸いです。
最後まで読んでいただいた方、ありがとうございました。