Web API の URI設計とパス設計

## はじめに

昨今「FE」と「BE」の分業が進んでおり、BEではViewを作成せず、FEで利用する`API`を作成することが増えてきたのではないでしょうか？

私自身も数年前と比べて`API`を作る機会が増えているのですが、最適なURI / パス設計ができているか不安になったため、「Web API: The Good Parts」を読むことにしました。

https://www.oreilly.co.jp/books/9784873116860/

この記事では、本書で学んだことを簡単にまとめています。

## URI設計

### URIとは？
URIってなんだっけ？と思った方はこちらの記事をお読みください。

https://ferret-plus.com/4637

### 良いURI設計とは？

- 覚えやすく、どんな機能を持つURIなのか一目でわかる

### 重要な事柄

URI設計の中でも重要とされている「6つ」の項目を見ていきましょう。

#### 1. 短くて入力しやすい  
`http://api.example.com/service/api/search` よりも `http://api.example.com/search` の方が、`URI`が簡潔です。


#### 2. 人が読んでも理解しやすい  
`http://api.example.com/sv/u/` のような形式は目的が理解できません。
世界共通で利用される英単語を用いることが好ましいです。

#### 3. 大文字/小文字が混在していない  
`http://api.example.com/Users/12345` は大文字と小文字が混在していています。
HTTPは大文字と小文字を厳密に比較するので、リクエストが誤ったパスにリクエストされてしまう可能性を引き起こします。

`http://api.example.com/users/12345` のように基本はすべて小文字を使用するようにしましょう。

#### 4. 改造しやすい（Hackable）なURI  
「`Hackable`」とは、URIの一部を変更して別のリソースを取得しやすい構造のことを指します。
例えば以下のURIを見てみると、ID部を変えると別のアイテムを取得できると推測可能です。
- 例：`http://api.example.com/v1/items/123456`  

このように、ユーザーから見てリソースの取得を理解しやすいパス設計を用いましょう。

#### 5. サーバ側のアーキテクチャが反映されていないURI  
例えば `http://api.example.com/cgi-bin/get_user.php?user=100` は「PHPでCGIが動いている」とわかるためこのましくありません。


#### 6. ルールが統一されたURI  
まずは悪い例を見ていきましょう。
- `http://api.example.com/friends?id=100`  
- `http://api.example.com/friend/100/message`  
複数形・単数形の混在、ID指定方法の不統一などルールが統一されていません。
利用するAPIごとにルールが異なると、ユーザーの混乱を招きます。


以下は良い例です。
- `http://api.example.com/friends/100`  
- `http://api.example.com/friends/100/messages`  
「友達」を複数形に統一、ID指定がパスに統一されています。


## エンドポイント設計

エンドポイント設計のポイントを見ていきましょう。

### 基本的なエンドポイント設計

以下はユーザーに関連する情報を操作するための基本的なAPI設計です。

| 目的                     | HTTP   | エンドポイント                                  |
|:-------------------------|:-------|:------------------------------------------------|
| 一覧取得         | `GET`    | `https://api.example.com/v1/users`              |
| 新規登録       | `POST`   | `https://api.example.com/v1/users`              |
| 特定ユーザの取得       | `GET`    | `https://api.example.com/v1/users/:user_id`     |
| ユーザ情報の更新       | `PUT`    | `https://api.example.com/v1/users`              |
| ユーザ情報の削除       | `DELETE` | `https://api.example.com/v1/users/:user_id`     |

作成されるAPIとしては5つありますが、エンドポイントは「`/users`」と「`/users/:user_id`」の2つのみです。

HTTPメソッドを指定することで、同じエンドポイントでも異なる処理を実行することができます。

#### HTTPメソッドについて  
| HTTP  | 機能               |
|-------|--------------------|
| POST  | リソースの新規登録 |
| PUT   | 既存リソースの更新 |
| PATCH | リソースの一部変更 |
| DELETE | リソースの削除 |

- HTML4の `<form>` タグでは、POSTのみが指定可能です。  
- HTML5のドラフトでは、PUTやDELETEを利用できる仕様が盛り込まれていましたが、仕様がまとまらず、議論の末に削除されました
- APIでは`PUT`, `PATCH`, `DELETE`メソッドの利用が可能なため、意図を明確にするためにもなるべく利用するようにしましょう

http://www.w3.org/wiki/User:Cjones/ISSUE-195

---

### 特定のレコードに関連するエンドポイント設計

以下は特定のユーザーに関連する「友達情報」を取得するエンドポイント設計です。

| 目的  | HTTP⠀⠀⠀⠀   | エンドポイント                                                      |
|:-----------------------|:-------|:--------------------------------------------------------------------|
| 友達一覧取得⠀⠀⠀ | `GET`    | `https://api.example.com/v1/users/:user_id/friends`                 |
| 友達追加               | `POST`   | `https://api.example.com/v1/users/:user_id/friends`                 |
| 友達削除               | `DELETE` | `https://api.example.com/v1/users/:user_id/friends/:friends_id`     |

友達情報はユーザに紐づく情報であるため、「**個々のユーザを表すURI（`users/:user_id/friends`）に紐づく**」形となります。

上記以降のパス設計については、ユーザ情報と同じで「`/friends`」と「`/friends/:friend_id`」の2つを利用します。

### エンドポイント設計の注意点

エンドポイント設計の中で重要とされている「4つ」の項目を見ていきましょう。

#### 1. 複数形の名詞を利用する

先ほどのエンドポイント設計でも「`users`」, 「`friends`」と名詞の複数形を利用していました。

`URI`はリソースを表すものという考え方であり、「`users`」, 「`friends`」は集合を表しているため、複数形である方が適切であるとされています。

#### 2. 利用する単語に気をつける

APIで利用する単語はなるべくネイティブな単語を用いることが好ましいです。

とはいえ筆者のようにネイティブでない場合、難しいかと思います。
その場合は、以下のようなAPIインデックスサービスを利用して、既存のAPIで利用されている英単語を確認すると良いでしょう。

https://apis.guru/

:::note warn
Web API: The Good Parts で紹介されている`ProgrammableWeb`は現在サービス提供を終了しているようです。
:::

#### 3. スペースやエンコードを必要とする文字列を使わない
URIでは利用できない文字（日本語文字列...）があり、そのような文字は「**パーセントエンコーディング**」と呼ばれる`%E3%81%82`のように`%`付きで表した表現方法を利用する必要があります。

これらの文字は、URIから意味が推測できないため利用しないべきです。
空白スペースも同様の理由で`+`に変換されるため、利用を避けると良いでしょう。

#### 4. 単語をつなげる場合はハイフンを利用する
エンドポイントの中で単語を2つ以上繋げる方法はいくつかあります。
- `https://api.example.com/v1/users/1234/profile-image` 
- `https://api.example.com/v1/users/1234/profile_image` 
- `https://api.example.com/v1/users/1234/profileImage` 

どれを使うべきか決定的な情報はない状況です。
そのため、迷った場合は以下の理由から「`-`」を用いるようにしましょう。
1. ホスト名では、「`-`」は利用できるが「`_`」が使えないため
1. 「`_`」は歴史上、タイプライターで下線を引くためのもので目的にそぐわない

また、単語を「`-`」などで区切るのではなくパス（`/`）で区切ることも検討しましょう。

## まとめ
以上です。
URI設計とパス設計のベストプラクティスに役立つことができれば幸いです。