158
144

API教に入信しよう【REST APIの教え】

Last updated at Posted at 2024-09-20

♪ピンポーン

あなたは今、幸せですか?
APIの教えを身につければ必ず救われます。
ぜひ話を聞いていってください。

APIが好き

Web API(以下「API」)って、それ単体で部品としていろんな人に使ってもらえたり、いろんなフレームワーク上で使われたりして、汎用性が高いのでとても好きです。
仕組みとしてはとてもシンプルですが、モバイル開発が普及した現在においてもかなり重要な役割を担っています。

対象読者と参考書籍

本記事の対象読者は、Web開発に関する基本的な知識がある方です。

「API(REST API)とは何か」や「HTTPってなに?」といった内容は含みません。

ただ、今回参考にさせていただいたオライリー・ジャパンの「The Web API」はそういった基本的な部分から応用まで幅広く記載されています。

古い書籍ですが、かなり役に立ちました。本当におすすめです。
今回は本書籍の1~3章の内容に触れます。

本記事においてのAPIは、一般的に REST API と呼ばれているものを指しています
上記の参考書籍に習って、RESTfulなAPIの設計に関する内容になります。

美しいAPI

本書では以下のAPIを「美しいAPI」と表現しています。

  • 使いやすい
  • 変更しやすい
  • 頑強である
  • 恥ずかしくない

どんなシステムにも言えることですが、使い心地の良いシステムに出会うと我々は好感を覚えて「美しい」と感じます。

APIにはいわゆるUIデザインというものはないため、視覚的な美しさが問われることはありません。
逆に言えば美しさをUIに頼ることはできないため、エンドポイントやリクエスト、レスポンスの設計に大きく左右されるのです。

さらに、APIは一般ユーザーではなく、どちらかというと我々と同業種であるエンジニアが利用することが多いです。
もし美しくないAPIを提供してしまうと、「誰だこんな汚いAPIを作ったやつは」と思われてしまう可能性が高いです。
それは、とても恥ずかしいです。

美しいAPIを設計するためにはどうすればいいでしょうか。

こういうものには大抵、先人の教えがあり、それを守っていれば美しいAPIができるものです。
本記事では先人の教えの一部を紹介します。

エンドポイントとリクエストの設計

HTTPメソッド

  • GET
    • 情報を取得する
  • POST
    • 情報を登録する
  • PUT
    • 情報を更新する
  • DELETE
    • 情報を削除する
  • PATCH
    • 一部の情報を更新する

例えば、指定したデータを削除するAPIをPOSTメソッドで実装することは可能です。
そのため、なんとなくGET以外はPOST設定する、もしくはGETすらもPOSTで設定する人もいるかもしれないですが、利用者の混乱を招く可能性があります。

特にPOSTとPUTをごっちゃにすることがあるかもしれませんが、これらは別物です。

PATCHは、PUTと同じく「情報を変更する」ためのものですが、PUTは指定したデータで丸々置き換えて変更するのに対してPACTHは「一部だけを変更する」らしいです。(PUTで代用できるのでほぼ使ったことないです。)

メソッドを適切に設定することで 何をするかが分かりやすいAPI になり、意図しないAPIの誤操作を防ぐことができます。

エンドポイント

Web開発をしているとURLのパスの設計をすると思いますが、APIでもエンドポイントの設計は重要です。
下記は、エンドポイントを決める際に気を付けること3つです。

  • 動詞を使わない
  • 複数形を使う
  • パラメータの渡し方

動詞を使わない

よく見るURIで/create-userといったものがありますが、これでは
/create-user
/fetch-user
/update-user
/delete-user
...
みたいな調子で、ユーザーデータを操作するだけで最低4つはエンドポイントを用意することになりますが、これは多すぎます。一つにまとめられます。

APIのエンドポイントは あくまでも対象のリソースを表すものだ と心に刻みましょう。

つまりcreateという動詞はデータに対する操作を表すので不要です。/usersだけにしてください。
なので正解は/usersです。

「いや、でもそれだとデータの取得なのか作成なのか、何をするのかが分かりにくいのでは?」
大丈夫です。そのためのHTTPメソッドです。

「対象のリソースをどうするか」はHTTPメソッドで判別します。

リソースとそれをどう扱うかを分離する考え方 はHTTPのものでもあります。

エンドポイントの数を最小に抑えられて利用者が分かりやすいだけでなく、
開発者としても名前に依存することなく 仕様を変更しやすいAPIができる と考えられます。

複数形を使う

エンドポイントはリソースを表すと述べましたが、そのリソースというのは大抵の場合、複数データの集合体ですよね?「ユーザー」とか「投稿」とか。

データベースをイメージしてください。UserテーブルにはUserデータが複数あるでしょう。

日本人なので簡単に複数形を無視して「カップヌードル」とか言っちゃいますが、正しくは「カップヌードル 」ですから!複数あるものは複数形で表してください。
/userではなく/usersです。

パラメータの渡し方

パラメータには、対象のリソースを絞り込む役割があります。
パラメータには2種類あります。
パスパラメータとクエリパラメータです。

/users/123のようにパスの一部として渡すパラメータがパスパラメータです。
例のように、IDなどの識別子を入れることが多いです。
これによって、「たくさんあるユーザーの中の、IDが123のもの」にターゲットを絞り込めるのです。

それとは別で、/users?id=123というのもありますよね。
こちらはクエリパラメータです。
絞り込みの結果は/users/123/users?id=123もおそらく同じでしょう。
じゃあどっちでもいいのかといえばそうではなく、

例えばユーザーの詳細情報を取得する場合など、一意な情報としてid情報が必須のときはパスパラメータを使い、

検索で一覧取得する場合など、省略できるオプションとしてid情報を含める場合はクエリパラメータを使うと良いでしょう。

この二つの場面は、同じデータを取得していますが状況が全くに異なります。
前者は「選択」で後者は「抽出」をしています。

もしどっちでパラメータを渡せばいいか迷ったら「必須ならパス、省略可ならクエリ」で考えましょう。(もちろん必須クエリパラメータを設計することもあります。)

レスポンス設計

オブジェクトの構造

例えばユーザーの詳細取得なら以下のようなレスポンスが考えられます。

{
    "id": 1,
    "name": "Yamada Taro",
    "birthday": "2000-09-10",
    "gender": "male"
}

ここに、所属する会社の情報を追加する場合、次のようにするとよさそうです。

{
    "id": 1,
    "name": "Yamada Taro",
    "birthday": "2000-09-10",
    "gender": "male",
    "company": {
        "id": 1
        "name": "株式会社〇〇",
        "address": "Tokyo..."
    }
}

companyというオブジェクトを用意してそこに会社の情報を入れていくと視覚的にわかりやすいです。
オブジェクトで包むのか、フラットにするのかはセンスが問われるところですが、そのAPIが使われる場面や、他の似ているAPIを参考にして決めていくとよさそうです。

また、上記のような会社情報の入れ方が冗長だと考えた場合は、以下のようにするのも良いでしょう。

{
    "id": 1,
    "name": "Yamada Taro",
    "birthday": "2000-09-10",
    "gender": "male",
    "company_id": 1
}

会社のidだけを返しておけば、続けて/company/1にアクセスして会社の詳細情報を取得することもできます。ただ、これだとフロント側で2回APIを叩くことになります。
このあたりのトレードオフを考慮して適切に設計したいところです。

配列

一覧取得などのエンドポイントは配列で返すのが一般的です。

[
    {
        "id": 1,
        "name": "Yamada Taro",
        :
    },
    {
        "id": 2,
        "name": "Tanaka Taro",
        :
    }
]

レスポンスはできるだけシンプルで見やすくする必要があるので、
以下のように配列にキーを指定して無理やりオブジェクトで返す必要はありません。

よくない例
{
    "users": [
        {
            "id": 1,
            "name": "Yamada Taro",
            :
        },
        {
            "id": 2,
            "name": "Tanaka Taro",
            :
        }
    ]
}

しかし、例えば一覧データの他に取得件数なども返したい場合はオブジェクトで返すといいでしょう。

{
    "users": [
        {
            "id": 1,
            "name": "Yamada Taro",
            :
        },
        {
            "id": 2,
            "name": "Tanaka Taro",
            :
        }
    ],
    "number": 10
}

エラーを知らせる

エラーは正常系の動きじゃないからといってあまり考えずに設定しがちですが、思い出してください。あなたはこれまで、どれだけエラーメッセージを頼りにしてきましたか?
利用者が失敗した時に優しく手を差し伸べられるAPIは、最高に美しいでしょう。

単純なパラメータのバリデーションエラーや、システム特有の権限エラーなどたくさんありますが、
利用者側の操作ミスによって起きうるエラーは全てエラーハンドリングして適切なエラーを返しましょう。

ステータスコード

HTTPではステータスコードというものが決められています。

ステータスコード 意味
100番台 情報
200番台 成功
300番台 リダイレクト
400番台 クライアントサイドに起因するエラー
500番台 サーバー側のエラー

ステータスコードの内容だけでも何本か記事が書けるくらい細かく存在しますが、本記事で伝えたいことは、とにかくステータスコードを正しく付けましょうということです。

WebシステムにおいてAPIを利用する場合、大抵の場合はレスポンスのステータスコードを見て成功か失敗かを判断します。
エラーなのにステータスコードだけ200とかになっていたらそのAPIは使い物になりません。

400番台と500番台がエラーのステータスコードです。

利用者がステータスコードを見ただけで、どこでエラーが発生したかが分からないといけません。

エラー詳細

ステータスコードに加えて、エラーの詳細も返しましょう。
よく使われているのはdetailmessageといったキーでその後の文字列でエラー詳細を示すものです。
具体的でかつ端的なメッセージを心がけます。

以下はトークン切れによる認証エラーの例です。

ステータスコード:403
{
    "error": {
        "message": "Bad authentication token",
        "info": "http://..."
    }
}

あえてトップレベルをerrorというオブジェクトで括っていますが、これはこのJSONレスポンスがエラーの内容であることを簡単に分かるようにするためです。

APIのレスポンスに載せきれない内容は別のドキュメントページなどに記載して、そのURLを返すのもありです。

まとめ

今回伝えたかったことは具体的な設計方法ではなく、
利用者思いの、利用者ビリティに溢れた美しいシステムを設計することが大事だということと、
それを実現するために設計思想や推奨される設計方法を学ぶ必要があるということです。

158
144
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
158
144