🐍

【本要約】Web API The Good Parts 1章~2章

2024/08/13に公開

概要

Web API The Good Parts について、個人的に大事だと思っている部分をまとめました。
今回は特に基本的な内容である1章、2章を書いています。

随時他の章も書いていく予定です。

1章

Web API とは何か

Web API とは、「HTTP プロトコルを利用してネットワーク越しに呼び出す API」のこと。
API とは、「機能は分かっているけれども、その中身の実際の動作は詳しく分からない(知らなくても良い)機能のカタマリを、外部から呼び出すための仕様のこと」を指す。

Web API は、人間がブラウザを使って直接アクセスすることを目的とした URI ではない。
例えば X(旧 Twitter) には、以下の URI が存在する。https://api.twitter.com/1.1/statuses/user_timeline.json

これにアクセスると、 HTML 形式ではなく、JSON 形式でレスポンスが返却される。
つまりこの URI は、ブラウザで直接表示することを前提にしたものではないということ。

このように、API はブラウザに人間が直接入力したり、リンクをクリックしてアクセスするものではなく、
データをプログラムが取得して他の目的に使うものであることを示している。

また、ブラウザからアクセスするものであっても、JavaScript を利用してデータを取得して加工して、
何らかの目的に利用するために公開されていれば、Web API と言える。

何を API で公開すべきか

もし自身のサービスで、API を公開していないのであれば、ビジネス観点からすぐにでも公開を検討すべき。
その際、何を API 経由で行えるようにすべきか。

それは、そのサービスのコアの価値のある部分をすべてを API 経由で利用可能にすべき。

例えば EC サイトを扱っているなら、商品の検索や購入、家計簿サービスなら家計簿の記入やこれまでの履歴の取得など、そのサービスが価値を生み出している部分全て。

Web API を美しく設計する重要性

以下が API を美しく設計したほうが良い理由。

設計の美しい Web API は使いやすい

API を設計するからには、できるだけ多くの人に使ってほしいはずなので、使いづらい API を公開してしまっては、そもそも API を公開する意味が薄れてしまう。

設計の美しい Web API は変更しやすい

API は自分たちとは関係のない第三者が使っていることも多く、その場合仕様が変わってしまうと、そうした人たちの作ったサービスがいきなり動かなくなってしまう状態になってしまう。

設計の美しい Web API は恥ずかしくない

開発者は他の開発者を、開発したコードやインターフェースなどの開発した成果物で判断する。
そのためセンスの疑われる API を公開してしまうと、そのサービスの開発者の技術レベルが疑われてしまう。

REST と Web API

REST API という言葉が一般的だが、本書では、「REST」という言葉は極力使用しないようにしている。
なぜなら本書が「良いパーツ」として紹介するルールが、REST という言葉の定義と、必ずしも一致しているわけではないから。

例えば REST における URI はリソースを表すため、極力 URI の中に動詞が入ることは良しとされない。
しかし、何かを検索する API のときは、「search」という単語が入っていたほうがわかりやすいことが多い。

このように、本書では現実的な Web API を「美しく」設計することに主眼を置いているため、REST という言葉を使って混乱を招くことを防いでいる。

2章

API エンドポイントの考え方

Web API におけるエンドポイントは、URI のことを意味する。
API は通常様々な機能がセットになっているので、複数のエンドポイントを持つことになる。

例えば、商品情報を取得する機能と、商品を購入する機能が API で提供されているとすれば、
それらの機能には、それぞれエンドポイント、つまり、「API としてアクセスできる URI」が存在することになる。

ユーザー情報を取得する API を作ったとして、https://api.example.com/v1/users/me のような URI を割り当てたとすると、これがエンドポイントになる。

モバイルアプリケーションなどの API 利用者は、このエンドポイントにアクセスすることで、 API の機能を利用できる。

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

良い URI の設計とは、覚えやすく、どんな機能を持つ URI なのかがひと目でわかる という原則がある。
一般的に重要な事柄は以下。

短く入力しやすい URI

以下の2つの URI は、基本的には同じ意味合いになる。

  • https://api/example.com/service/api/search
  • https://api/example.com/search

同じことを表しているなら、後者のように、シンプルで理解しやすく、覚えやすいほうが良い。

人間が読んで理解できる URI

`http://api.example.com/sv/u
「sv」や「u」が何を示しているか分かりづらい。

また、API で良く使われている単語を使うことも重要。
検索 API で良く使われる単語は、「search」であり「find」ではない。

大文字小文字が混在していない URI

基本的にはすべて小文字を使うこと。
標準的に使用されているのは小文字。

改造しやすい(Hackable な)URI

http://api.example.com/v1/items/123456
上記のように、「123456」の部分を変えれば、「別のリソースにアクセスできそう」と予測できる。

サーバ側のアーキテクチャが反映されていない URI

http://api.example.com/cgi-bin/get_user.php?user=100
上記の URI は、「PHP で書かれていて、CGI で動いているんだろうな」と予測できてしまう。
このような情報は、利用者的には全く必要なく、API の脆弱性も高めてしまうので、避けたほうが良い。

ルールが統一された URI

同じサービスで以下の2つの URI で API が提供されていたとする。

  • http://api.example.com/friends?id=100
  • http://api.example.com/friend/100/message

後者では「message」が単数形になっていて、統一されていない。
こうなっていると、クライアントが実装する際に混乱を招きやすいので、問移しておいたほうが良い。

API のエンドポイント設計

API のエンドポイントは、URI の設計と HTTP メソッドを組み合わせて考えることができる。
例えば、以下の機能を API で提供するとする。

  • ユーザー登録
  • 自分の情報の取得
  • 自分の情報の更新
  • ユーザー情報の取得
  • ユーザーの検索

上記の機能は以下の5つのエンドポイント設計でカバーできる。

目的 エンドポイント HTTP メソッド
ユーザー一覧取得 http://api.example.com/v1/users GET
ユーザーの新規登録 http://api.example.com/v1/users POST
特定のユーザーの情報の取得 http://api.example.com/v1/users/:id GET
ユーザーの情報の更新 http://api.example.com/v1/users/:id PUT/PATCH
ユーザーの情報の削除 http://api.example.com/v1/users/:id DELETE

上記は API としては5つだが、エンドポイントとしては2種類になっている。
違いはv1 から先が/users/users/:idかであり、これらはそれぞれ「ユーザの集合」と、「個々のユーザー」を表すエンドポイントである。

この2つの概念は、データベースのテーブル名とレコードの関係と言える。
そのテーブルやレコードに対して、どのような操作を行うかということを、 HTTP のメソッドで表現している。

例えば、

  • テーブル(/users)に対して GET を行っているので、そのテーブルの一覧を取得できる API だ
  • レコード(users/:id)に対して DELETE を行っているので、そのレコードを削除できる API だ

ということが、エンドポイントと HTTP メソッドを見ただけで表現できる。

リソースにアクセスするためのエンドポイントの設計の注意点

複数形の名刺を利用する

例えば以下のエンドポイントがあるとする。
https://api.example.com/v1/user/12345

user が単数形になっているが、データベースのテーブル名が複数形のほうが適切であるのと同様に、users は、「集合」を表すので、複数形のほうが適切。

検索とクエリパラメータの設計

例えば以下のようなユーザー一覧取得のエンドポイントがあるとする。
http://example.com/v1/users

このとき、例えば1万人のユーザーをすべてを取得するわけにはいかない。
データサイズが大きすぎるからである。

なので、一度に取得可能な人数の上限を決め、ページングを行ってデータを取得するようにする必要がある。
いわゆるページネーションと呼ばれる仕組みを実現する必要がある。

取得数と取得位置のクエリパラメータ

ページネーションの仕組みを実現するうえで、「取得数」と「取得位置」を考える必要がある。
これには代表的な組み合わせが決まっていて、以下の2種類である。

  • per_page と page
  • limit と offset

それぞれ、1ページ50アイテム、3ページ目(101アイテム目から)を取得する場合は、以下のようになる。

  • per_page=50&page=3
  • limit=50&offset=100

相対位置を利用する問題点

page=3offset=100 のように、相対的な取得位置(全データセットから何番目を取得するか)でデータを取得する方法には、以下の2つの問題点がある。

  • 先頭から「何番目か」を調べるために、先頭からの数を数える処理が行われるため、パフォーマンスが悪くなる
  • 前に取得してから、次のデータセットを取得するまでにデータの更新が入ってしまうと、データの不整合が起きる

絶対位置でデータを取得する

相対位置の問題点を解消する方法として、「絶対位置で取得できるパラメータを用意しておく」というのがある。
ただし、「先頭から数えて何件」ということではなく、「指定した ID や時刻よりも前」といった方法で指定を行う。

これまで取得した最後のデータの ID や時刻を記録しておいて(レスポンスなどで返して)、「この ID よりも前のもの」や「この時刻よりも前のもの」といった指定を行い、エンドポイントにアクセスする。

X(旧 Twitter)の API では、max_id といったパラメータが用意されており、指定した ID 以前のものを取得するようにしている。

絞り込みのためのパラメータ

絞り込みとは、いわゆる検索のためのパラメータである。
複数の項目での絞り込みのパターンの場合、以下のようなクエリパラメータがある。

  • ?first-name=Clair
  • ?last-name=Standlish
  • ?school-name=Shermer%20High%20school
    この場合はクエリパラメータ名には絞り込む要素名、値には絞り込む値を指定し、複数ある場合はそれらすべてを指定するようにしている。

一方で検索するフィールドが1つに決まっている場合は、qというパラメータが使われることが多い。

  • q=jack
  • q=Tokyo

絞り込みを行うクエリパラメータでは、何の検索対象から、どんな検索(全文検索、部分一致、完全一致など)をよみとれるようにするのも重要。
以下のようなクエリパラメータでは、絞り込み対象は名前に限定されると直感的に伝わる。
http://api.example.com/v1/users?name=ken

また、以下のようなクエリパラメータでは、全文検索、つまりユーザーの情報の中で、文字列情報が含まれるフィールドすべてで ken という単語が検索されるという意味にとれる。
http://api.example.com/v1/users?q=ken

Google の検索もクエリパラメータ名は q であり、指定した単語がどこかに含まれるページが検索結果として返る。
Instagram では、q で指定するのは名前であるとドキュメントに明記されているので、検索対象は名前だけだし、ユーザーの検索の場合は名前だけが検索対象というのは直感的にわかりやすい。しかし、検索対象によっては全文検索のほうが直感的であり得るので注意することが必要。

URI に「Search」という単語はいれるべきか

http://api.twitter.com/1.1/search/tweets.json
上記のようなエンドポイントには、searchという単語が入っている。
「検索する」という行為は動詞であり、URI をリソースと表すものとするデザインでいえば王道から外れてしまう。

しかし、わかりやすさの観点で、「この API は検索のためのものであり、全リストを取得するためのものではありませんよ」ということを示すのであれば、search という単語を入れるのは「あり」である。

例えば膨大な量のデータが存在する場合、全件取得することは数が多すぎて現実的でない。

クエリパラメータとパスの使い分け

クエリパラメータにいれる情報は、URI のパスに含めることは設計上は可能である。
クエリパラメータにいれるか、パスに含めるかの判断基準は以下である。
一意なリソースを表すのに必要な情報かどうか
これは URI がリソースを表すものであるという思想から来ている。
例えば http://api.example.com/users/:idという URI の場合、ユーザーの ID を指定することで参照するリソースは一意に定まるため、パスに入れたほうが良い。

省略可能かどうか
例えばページネーションのときの page, offset, あるは limit などは、クエリパラメータで指定しない場合、デフォルトの値が利用されるケースが多い。
このような指定しなければデフォルトの値が使われるようなときにはクエリパラメータのほうが良い。

まとめ

  • Web API とは、「HTTP プロトコルを利用してネットワーク越しに呼び出す API」のこと
  • API を美しく設計する重要性は以下
    • 使いやすい
    • 変更しやすい
    • 恥ずかしくない
  • 良い URI の設計とは、覚えやすく、どんな機能を持つ URI なのかがひと目でわかるという原則がある
  • ページネーションありの API を設計するときのクエリパラメータは per_page & page または limit & offset
  • ページネーションの設計のときは、相対位置でデータを取得する弊害についても考慮すること
  • クエリパラメータとパスの使い分けには、一定の判断基準が存在する

Discussion