さあ、API設計を始めよう!
概要
このページはオライリージャパンの「Web API The Good Parts」を読んで学んだことと業務で学んだことをメモ変わりに記載しているページです。
また上から下に読み進めていくとAPIの設計を行うことができるような構成にしたいと思っているので、API設計の虎の巻みたいにできたらいいと思ってます( ◠‿◠ )
1.どんなAPIを作成したいのか考え整理しよう
API設計のSTEP1として、どんなAPIを作成したいのか考えましょう。
DBにアクセスしてデータを取得するのか、外部APIにリクエストするのか、色々な用途があると思います。APIを設計する上でどういったことをするのか考えて整理しましょう。
今回はTodoアプリを例に考えていきたいと思います。
- やりたい・必要な機能
- Todoの取得
- Todoの検索
- Todoの登録
- Todoの編集
- Todoの削除
- ユーザーの登録
- ユーザーの編集
2.APIのエンドポイント(URL)を考える
APIでやりたいこと・必要な機能を整理したら、そのAPIにアクセスするURLを考えましょう。
作成する上で、覚えやすくどんなURLなのかひと目でわかる ことを意識して考えるようにしましよう。意識する上で大切なことを下記に記載します。
- 短く入力しやすい
- 人間が読んで理解できる
- 大文字小文字が混在しない
- 改造しやすい
- サーバ側のアーキテクチャが反映されていない
- ルールが統一されている
- 複数形の名詞を利用する
- 利用する単語に気をつける
- スペースやエンコードを必要とする文字を使用しない
- 単語を繋げる必要がある場合は、ハイフンを利用すること
大体の項目は理解できると思いますが、一部補足を記載します。
改造しやすい
ここでいう改造とはURLを修正することで別のURLにすることができることを意味します。
例えば、http://api.example.com/v1/todos/1 このURLの末尾の1を変更するだけで違うtodoにアクセスすることができることがイメージできると思います。
これが100までは、http://api.example.com/v1/todos/1 でアクセスできても、100以降はhttp://api.example.com/v1/todos2/101 でないとアクセスできないと使用する目線では使いづらく思わぬ事故が発生してしまう可能性があります。
サーバ側のアーキテクチャが反映されていない
これはURLからアーキテクチャがわかるようにしないことを指しています。
例えば、http://api.example.com/v1/todos/edit.php?todo=1 このURLではphpで作成されていることがわかってしまうと思います。
アーキテクチャがURLからわかってしまうとセキュリティに危険が発生する可能性がありますので、やめた方がいいでしょう。
ルールが統一されている
実際に業務で設計する際には、すでにAPIがある場合がほとんどだと思います。
その場合、他のAPIやドメインに合わせたURLにすることで統一感が出て使いやすいAPIになります。
※もちろん過去の設計がイけてない可能性もあるので、よく考えることも必要だと思います。
3.HTTPメソッドも一緒に考えよう!
URLを考える際に、一緒にHTTPメソッドも考えましょう。
これを読まれている方に説明は不要だと思いますが、HTTPメソッドの一覧を記載しておきます。
イメージとして、URLは名詞でHTTPメソッドは動詞と考えるとどのHTTPメソッドにすればいいのか考えやすいと思います。
メソッド名 | 説明 |
---|---|
GET | リソースの取得 |
POST | リソースの新規登録 |
PUT | 既存リソースの更新 |
DELETE | リソースの削除 |
PATCH | リソースの一部変更 |
HEAD | リソースのメタ情報の取得 |
APIのエンドポイント(URL)設計の実施
上記URLとHTTPメソッドの説明から今回の例のTodoaアプリのURLを設計していきたいと思います。
目的 | URL | メソッド |
---|---|---|
ユーザーの登録 | http://api.example.com/v1/users | POST |
ユーザーの詳細情報取得 | http://api.example.com/v1/users/:id | GET |
ユーザーの編集 | http://api.example.com/v1/users/:id | PUT/PATCH |
Todoの取得・検索 | http://api.example.com/v1/users/:id/todos | GET |
Todoの登録 | http://api.example.com/v1/users/:id/todos | POST |
Todoの詳細情報取得 | http://api.example.com/v1/users/:id/todos/:id | GET |
Todoの編集 | http://api.example.com/v1/users/:id/todos/:id | PUT/PATCH |
Todoの削除 | http://api.example.com/v1/users/:id/todos/:id | DELETE |
※URLのドメインは適当に記載しています
上記のURLを設計してみました。
URLは同じでも異なるHTTPメソッドを使用することで、一つだけでなく様々な表現を行うことができるようになります。URLをうまく使えていると思います。
ただし今のURLではuserのidを適当な値を入れると赤の他人のTodoも操作できてしまうので、別途セキュリティを考える必要があります。(アクセストークンやセッション)
さてここまで大体の設計ができたと思いますが、Todoの取得・検索のクエリパラメータの設計を考える必要があります。
検索とクエリパラメータの設計
クエリパラメーターを考えるあたり、対象のURLを確認します。
上記のURLにクエリパラメータを設計していきますが、どんなパラメータを設定するべきでしょうか?
今回の例のTodoアプリでは、一度に表示する数とどこから値を取得するのかがパラメータとして必要そうです。
もしこの二つのパラメータがない場合、Todo全件取得することになってしまうため、データが大きくなった場合にパフォーマンス上で問題が発生する可能性があります。
そのため、上記パラメータを持ちいる必要が出てきます。クエリパラメータを追加して例を記載します。
limitはデータの取得数、pageは取得する位置を表しています。
クエリパラメータの名前は各社色々な名前を使用しているので、調べてみるのがいいと思います。
さて、これでクエリパラメータの設計は完成だと思いますが、実はまだ問題&考えることが残っています...
まず一つ目がパフォーマンスの問題です。
先程パフォーマンスの問題が解決した的なことを記載しましたが、実は上記のやり方では別の方面でパフォーマンスの問題が発生してしまいます。それはpageのパラメータからDBから値を取得する際に、SQLでoffsetとlimitを使用してデータの位置を取得しようとすると、データの数が増えるにつれて速度が遅くなってしまうことがあることです。
offsetとlimitではデータの一番最初から数えてデータの位置を数えるため問題が発生してしまいます。(相対位置)
そこで、このIDから50件のデータを取得してほしいといった形に修正することで、わざわざはじめからデータの数を数える必要はなくなり問題を解決することができます。(絶対位置)
そのためには、クエリパラメーターに絶対位置を追加し修正します。これで問題を解決することができました。
二つ目は絞り込みを行うためのパラメータです。
複数のパラメータを使用可能にするのか、絞り込みのためのパラメータがない場合はエラーを返すのか考える必要があります。各社決まった絞り込みのためのパラメータは存在しないので、ここも調べてみるのがいいと思います。
ここでも検索クエリを使用した例を記載してみます。
以上でクエリパラメータの設計を完了したいと思います。
4.レスポンスデータの設計
さて、APIのリクエスト設計ができたら、次にどういったレスポンスを行うのか考えます!
(APIの処理については、サマざなケースがあると思うので今回は省略します)
設計することとしては、以下があると思います。
- レスポンスするデータのフォーマット
- レスポンスするデータの内部構造
レスポンスするデータのフォーマット
レスポンスするデータのフォーマットですが、昨今はJSONが主流だと思います。
XMLを使用するところもあると思いますが、基本的にはJSONを使用するところが多いいと思います。
もしどちらも対応できるようにする必要があれば、リクエスト時にデータフォーマットを指定してもらう必要があります。方法としては3つあります。
-
クエリパラメータを使う方法
http://api.example.com/v1/users/:id/todos?format=xml
URLの後ろにフォーマットを指定しています。 -
拡張子を使う方法
http://api.example.com/v1/users/:id/todos.json
URLの後ろにjson形式を指定しています -
リクエストヘッダでメディアタイプを指定する方法
Accept: application/json
Acceptは受け取る時のデータ形式を指定する方法
上記の方法の中で各社はクエリパラメータを使用しているケースが多いいみたいです。
ここはチームメンバーに相談しながら実装した方が良さそうですね。
レスポンスするデータの内部構造
次にレスポンスするデータの内部構造を考えていきます。
データの内部構造を検討する際に以下の点を気をつけます。
- APIのアクセスが増えるような構造にしない
- 目的に応じたデータを返却すること
- データはフラットにすること
APIのアクセスが増えるような構造にしない
APIからのレスポンスでは必要な情報が足りず、再度別のAPIにアクセスすることはスマートじゃありません。無駄にリクエストが増え、パフォーマンスに問題を与えるかもしれません。
一度のリクエストで欲しいデータが全て手に入るような設計にすべきだと思います。
目的に応じたデータを返却すること
1度のリクエストで欲しいデータが手に入るようになるように設計した後、データを余計に渡していないか確認することが大切です。例えば、タイトルと作成日のデータしかいらないのに、カテゴリや更新日などいらないデータをもらってもデータの量がただ多くなってしまいます。
この無駄を防ぐにも必要なデータしか返却しない方がいいです。
具体的な方法としては、リクエスト時に欲しいデータをリクエストパラメータとして送る方法があります。以下のようなURLでリクエストすることで、nameとageのみ取得できるような柔軟はAPIを作成することができます。
データはフラットにすること
データ形式をJSONに決めたあとは、データの内部構造を決める必要があります。
このデータ構造はGoogleのJSON Style Guideで、なるべくフラットな方がいいと記載がありますが、ケースバイケースといった感じのことが記載されており、決まったルールはないように感じます。
筆者は受け取る側のことを考えできる限りわかりやすいデータ構造にすべきだと思います。いくつか例を記載します。
データ例1
{
"id": 1,
"title": "example",
"body": "example",
"category": 2,
"importantFlg": true,
}
データ例2
{
"todos":[
{
"id": 1,
"title": "example",
"body": "example",
"category": 2,
"importantFlg": true,
},
{
"id": 2,
"title": "example2",
"body": "example2",
"category": 1,
"importantFlg": false,
}
]
}
もしページネーションがあるようなデータを返却する際は、データの最後にhasNextといった項目を追加し、trueかfalseで返却するようにすれば、次のデータあるか判定することができます。
レスポンスデータの項目名のフォーマットについて
さて、ここまでの段階でレスポンスするデータについて大体の設計ができてきたのではないかと思いますが、データの項目名についても注意したいことがあります。下記に気を付けてたいことを記載します。
- 多くのAPIで同じ意味になるように利用されている一般的な単語を用いる
- なるべく少ない単語数で表現する
- 複数の単語を連結する場合、その連結方法はAPI全体を通して統一する
- 変な省略形は極力利用しない
- 単数形/複数形に気をつける
リクエストについて設計する時と同じようにデータ項目名も上記の内容を気を付ける必要があります。
エラーの際のレスポンス
APIを実行した際にエラーが発生する機会があるかもしれません。その時用にエラーが発生した時用のレスポンスを考える必要があります。
必要なものとしては、ステータスコードでエラーの大まかな原因を表現して、エラーメッセージで詳細な内容を伝えてあげるのがいいかと思います。
ステータスコードの分類
ステータスコード | 意味 |
---|---|
100番台 | 情報 |
200番台 | 成功 |
300番台 | リダイレクト |
400番台 | クライアントサイドに起因によるエラー |
500番台 | サーバーサイドに起因するエラー |
エラーメッセージについて
エラーメッセージをレスポンスする方法は、リクエストヘッダに独自の要素を追加する方法とリクエストボディにエラーメッセージを格納してレスポンスする二つの方法があります。
各社リクエストボディに格納するのが一般化しているので、ここはリクエストボディに格納するを選択していいと思います。
肝心のエラーメッセージですが、提供するAPIによって異なる部分があると思いますが、基本的にはエラーメッセージと調査する際に有効なドキュメントなどの情報をレスポンスするのが基本となると思います。あとは実際の業務で他のAPIと比較しながら作成するのがいいと思います。
..ここから先は筆者の時間がある時に追記していきたいと思います( ・∇・)
Discussion