のんラボ

RESTful APIを実用的に使うためにはどうすればいいだろうか?

2019/10/11 2019/10/16 RESTful APIを実用的に使うためにはどうすればいいだろうか?

RESTful APIを実用的に使うためにはどうすればいいだろうか?

こんにちは。のんです。

実は 【大阪・本町】smaregi tech talk #1 【Web API】 にLT枠で登壇することになりまして、このために再度REST APIについて纏めていました。

何番煎じかわかりませんが、お付き合いください。

スライドはこちら

ちょっと絵文字が崩れてるけど許してください🙇
GoogleSlidesを使ったのですが、うまいこと行く方法があれば教えて下さい

REST APIは実務で使うとちょっと悩むところがある

WEB APIをRESTで設計するとめちゃくちゃ簡単に設計と作成することができますが、
いざ作成してみると、「こういうときどうすればええねん」みたいなときが多くなります。

今回はそういったちょっと考えちゃうところの解決案を提示していこうかなと思います。

よくある簡単なやつ

めちゃくちゃ簡単ですね。
強いて挙げるとすれば、バルク参照でしょうか。

個人開発などの小規模アプリでは1個の参照で十分という場合が多いです。
多くても10個とかなので、それでも何回かリクエスト飛ばして対応することも多いでしょう。

しかし業務アプリのAPIとなると数千の参照を一発で解決したいという要望は結構多いです。(少なくとも僕の周りでは)

そこでapi/products/{:id}で対応するのではなく、api/products/{:id},{:id},{:id}・・・で対応するバルク参照があります。

結構思いつく方も多そうなので、スライドでは言及していませんが、一応実務的な内容なので、挙げておきます。

このとき注意したいのはmysqlなどで一括取得するときのIN句には限界があるところや、レスポンスの容量が大きすぎないよう調整をかけるところでしょうか。

実際には・・・

想像するように実際には中間テーブルや関連テーブルが腐るほどつながっているテーブルが多いですよね。

しかも、その関連は更新がある度に判定・更新をしなければならない仕様が多いので、RESTでリソースにアクセスする際に中のコードで嫌な設計になってしまいがちかもしれません。

自作アプリでも簡単な内容ですが、似たようなことがありました

僕が作成している個人開発のアプリ トドTask でもAPI作成時に似たようなことがありました。

タスクとノートをそれぞれ独立させて作成できる機能があったのですが、タスクのメモ代わりとしてノートに書くというユースケースがありましたので、タスクとノートを紐付けて管理する機能を新規追加していたときのことです。

タスク追加のAPIでノートも一緒に登録して、紐付けしたいと僕は思いました。

でも単純な考え方をすると

copied.api/tasks
api/notes
api/tasks_notes

の3つのエンドポイントを用意して登録処理をしなければならないと考えてしまいました。

まぁでもそうではありませんよね。

解決策① テーブル=リソースではない

DBに用意されているテーブルそのものがリソースとは限らない点に気づきました。
MVCにおけるModelのように、複数のテーブル情報を組み合わせて、1つのリソースと考え、エンドポイントを1つにまとめることができます。

こうすることで、

copied.api/todo

に対して

copied.{
    "task": "新しいアプリを作る",
    "note": "LINE Bot を使いたい",
    "status": "1",
}

というリクエストボディを送信することでtasknoteを登録・更新することができます。

解決策② 煩わしいAPIにしない

解決策①は纏めてしまうという考え方ですが、こちらは連結させてしまうという考えです。

よくありそうなレスポンスが、成功時に成功メッセージだけを送信して終了するみたいなエンドポイントがありますが、そういったものは控えたほうがいいでしょう。

間違いではありませんが、API使用者側がこのレスポンスを使用して次のアクションにつなげにくいです。

登録成功時には、そのリソースに関わる現在の情報を返してあげれば、その情報を使って次のアクションをお越しやすいです。
こういう風な情報をキャッチボールするとよりAPIを使用しやすくなり、ユーザーのためにナリやすいですね。

今回の場合はidを流用してnote_idとして使いまわします。

リクエストを2回送信することになりますが、シンプルな実装になりそうです。

まぁでも解決策①のほうがスマートで良いですね

WEB APIでサービスを起動したい

REST APIなのでエンドポイントは名詞ですよね。

でも昨今台頭してきている機械学習APIなどはどう考えてもリソース(つまり名詞)ではなくサービス(つまり動詞)です。

こういうときREST APIだとエンドポイントに悩みます。

解決策 RESTにこだわりすぎない

例えば検索サービスなどをAPIにするときはこうなってしまいそうで怖いですね。

copied.api/search
copied.{
    "users": {
        "name": "Z250"
    },
    "tasks": {
        "name": "Z250"
    },
    "notes": {
        "name": "Z250"
    }
}

このようなjsonではなんかRESTっぽくないしイケてない。

そういうときはこうしてみてはいかがでしょうか?

REST APIの目的はAPI使用者にわかりやすいエンドポイントを作成することです。
RESTにこだわりすぎてわけわからんエンドポイントを作成してしまっては本末転倒なので、注意しましょう。

当たり前ですが、エラー処理

当たり前ですが、エラー処理はHTTPステータスコードを使用しましょう。

「当たり前やんけ」と思った方、そうは思っても実際にエラーコードをボディに書いてしまったことは無いでしょうか?

大規模になるほど、エラーも独自仕様になりやすくなってしまいます。
心を鬼にして、デファクトスタンダードを進みましょう。

よく使うHTTPステータスコードです。

エラーメッセージは最低限を返すようにしましょう。
エラーメッセージはAPI使用者が任意に変更したいはずですから。

最後に

  • フロント言語の台頭でAPIは更に重要に
  • 機械学習・統計のネタにされやすくなる
    • 食べログ
    • 天気予報
    • 図書館
  • リソースではなくサービスの提供もできる

この辺は抑えておきたいですね。
Googleなんかは機械学習用のデータAPIや、機械学習行った結果を返すサービスAPIなども用意しています。

APIが当たり前の時代がやってきました。
皆さんが作るアプリにはWEB APIが実装されているでしょうか?

僕のアプリも絶賛開発中です。
もし僕のAPIを利用するときはよしなに。

.