タグ:WEB API
Web APIをどう解釈してる?
「そもそもAPIって何?」って質問をされた時にGoogleMapやFacebookのAPIの使い方を説明するのがわかりやすいと思います
それと同義に世間一般のWeb APIの概念として広まったので通信プロトコルアーキテクチャのスタイル=RESTという盲目的な認識でREST形式以外があまり浸透していないのではないでしょうか?
サービスのコア機能を非同期で手軽に利用したり、フロントとバックの独立する設計=API(盲目的にREST)という大きな誤解をしないためにも掘り下げます。
代表的なWeb APIの種類
- XML-RPC
- 最も古く、最も単純なプロトコル。拡張市場言語 (XML) を使用してコマンドをエンコードさせるのが特徴。
- JSON-RPC
- XMLメソッドと非常によく似ていますが、データを転送するためにJavascriptオブジェクト表記(JSON)形式を使用
- JSONを使ったシンプルなやりとりになるため、Webブラウザやモバイルアプリなど、近年のマイクロサービスにおけるサーバー間の通信に使われるスタイル
- SOAP
- Simple Object Access Protocolの略。異なるコンピュータ上で通信をXMLでやりとりする事をメインとしており、医療や交通や金融といった分野のレガシーシステムでメインに使われるスタイル
- REST
- レスポンス形式の仕様というよりもサーバーがコマンドを認識して要求して、条件に合うステートレスなプロトコルを返すために制約を課すアーキテクチャ的なスタイル
今回はWebアプリケーションサービスのAPIアーキテクチャとして、代表的と呼べるREST形式とRPC形式を掘り下げます。
RPC(Remote Procedure Call)について
- 「リモート・プロシージャ・コール」の略。
- 別のシステムから関数を呼び出すというプログラム言語の手続き型の流れと本質は同じ操作要求スタイル
- プログラムが単一のマシン間の境界を意識することなく通信を可能にする。実例としてはgitを入れたローカルマシンから
push origin master
コマンドでGithubにプッシュする手段 - 一般的なRPCの概念はこの手段をHTTPを経由して受信するRPC over HTTPの事として取り上げていると思われる(Git ⇔ Githubは基本HTTPではなくシェル操作のSSH通信)
# メンバーID4の名前を取得
> curl http://example.com/getMemberNameById?id=4
{"name":"山田太郎"}
- 基本的にパラメータを必要としない(何も変更しない)操作はGET、それ以外はPOSTで捉える
RPC形式のメリット
- 複数言語に渡るプログラム間で条件分岐する場合でも直接メソッドでの操作が可能
- 仕様によってはGETかPOSTかすら気にせず、すべてPOSTで対応可能なシンプルさ
- メソッドが直接エンドポイントに作用するので機能追加がしやすい
- ペイロードが軽量になるので最善なパフォーマンスがで出やすい
RPC形式のデメリット
- メソッド名を直接窓口にするため、クライアント側でもエンドポイントとなるバックエンドのメソッド名を把握していなければならない&似たようなメソッド名で事故りやすい
- エンドポイントとしてメソッド名を直接晒すことになるのでセキュリティ問題が常につきまとう
- リクエストした単一メソッドからのコールバックが基本構造になるため、条件に合わないリクエストの際に適切なレスポンスをする構造に難が出る
- 例)ユーザー「Aさん呼び出して」(/active?user=A) → サーバー「いません」
- "Aさんがそもそも存在するのか?"や"何時ならいるか?"や"他に誰がいるか?"の臨機応変なレスポンスが実装しにくい
- RESTのようにパスによる親子構造が構築できないので機能の発見性が低い&整理しづらい
GoogleのgRPCについて
gRPCは上記のRPCスタイルを取り入れた上でhttpメソッド一覧を独自プロトコルファイルを参照窓口にさせてデータをシリアル化してくれることで生データのやりとり基準のRPCにSSL/TLSの暗号規格も組み込んだ温故知新なGoogle様の神ツール。
現在のRPCスタイルはgRPCの利用を前提という認識の模様
REST(REpresentational State Transfer)について
- 「レプレゼンテイショナル・ステート・トランスファー」の略。
- 一連の制約(HTTPメソッド)とガイドライン(パス命名ルール)によるリソース要求スタイル
# 山田家メンバー一覧取得
> curl http://example.com/yamada/member/
{"name":"太郎", "name":"次郎", "name":"三郎", "name":"四郎"}
# ID4の四郎の情報を取得
> curl http://example.com/yamada/member/4
{"id":4, "name":"四郎", "sex":"male", "age":"10" }
- Resource Oriented Architecture(リソース指向アーキテクチャ)を原則とした設計
機能自体には関係無く、あくまでリソースの呼び出し結果の定義
RESTの定義と設計上の性質
- 名前(URI)を通じて限定的&段階的に指し示すことができる性質を有していること
- APIのバージョンやデータの操作条件がひと目でわかる(予測可能である)
- API Ver.1のユーザー検索「/v1/search/user」+POST keyword="四郎"
- API Ver.2のユーザーID3番の情報を取得「/v2/user/3」
- すべてのHTTPリクエストが完全に分離し、ステートレスであること
- セッションなど、API側でシステム状態管理を干渉させない
- HTTPプロトコルが適応されること
- HEADタグを利用し、Last-ModifiedタグやE-Tagを使うとキャッシュ(アクセス回数やブラウザとサーバーの更新確認)の実装が可能になる
- インターフェースが統一されていること
- 操作をHTTPメソッドに限定(登録はPOST、取得はGET、削除はDELETEなど)
REST形式のメリット
- フロントエンドで使われる技術をそのまま使える(JSが扱いやすいJSONベース)ので汎用性が高い
- HTTPプロトコルによって一貫性かつ互換性のある操作を行える(登録はPOST、取得はGET、削除はDELETEなど)
- URLを検索することで、URLの機能とそのレスポンス情報を解読しやすい&エンドポイントを見ればどういうデータが行き交ったのかが理解しやすい
- サーバー側とは受け取るリソースのみの認識共有で済むので、フロントとバックがそれぞれの相互依存気にせず開発に集中できる
REST形式のデメリット
- 概念自体が状態保持をしないので認証系ではRESTの本質を捨てざるを得ない場面が出てくるためRESTfulの例外となるリクエストケースが生まれる(Cookieなどを使ったセッション管理の場面など)
- RESTはRPC初期のサーバーに直接メソッドを呼び出す手法ではなくHTTP通信によるヘッダーに付与した送信するメタデータ量が多くなる&テキストベースで転送率が悪いため、パフォーマンスの低下が考えられる
- プログラミング言語自体はリソース指向ではないので、リクエストの幅が広くなるほどURIとマッピングするコードは汚くなりがち
- RPAの原則に従うものの、それなりの自由度が残るので個人間の思想の相違やバックエンド側までが"優れたREST設計"理論に巻き込まれやすくなりサービスの本質ではない部分に検証作業などの時間がかかる
GraphQLはこのRESTのスタイルをより限定的かつシンプルな仕様をスキーマ言語で必要なデータにのみリクエストするクエリ言語のAPI規格
RPCは「動詞」、RESTは「名詞」の観点でリソースを定義
それぞれの特徴を理解したうえで、実装としての2形式の違いを一番理解しやすいのはリクエストのエンドポイントです。
# 注文する(POST)
RPC: http://example.com/Orders/PlaceOrder
REST:http://example.com/Orders/Order?OrderNumber=asdf
# --> POST:{オブジェクト}
# 注文の取得(GET)
RPC: http://example.com/Orders/GetOrder?OrderNumber=asdf
REST:http://example.com/Orders/Order?OrderNumber=asdf
# 注文の更新(PUT)
RPC: http://example.com/Orders/UpdateOrder
REST:http://example.com/Orders/Order?OrderNumber = asdf
# --> PUT:{オブジェクト}
- RPCはメソッド名を直接リクエストする性質上、「リクエストに応えるレスポンスを操作する」という動詞の定義
- RESTはGETメソッドは安全なリクエストである事が前提なので、DELETEなどの動詞メソッドは使わず、パスごとに区切った「リソース単位でレスポンスを要求する」という名詞の定義
※どちらも定義としての例なので、こうしなければならないという事ではありません。
DropBoxがAPIの仕様をRESTからRPCに戻した理由
クラウドストレージサービスのパイオニアとも言える「DropBox」が2010年からサービスAPIを提供しており、こちらがVer.1ではREST APIを採用していたにも関わらず、Ver.2からはRPC形式に仕様が変更になりました。
リクエスト |
Ver.1 |
Ver.2 |
ユーザー情報 |
/1/account/info |
/2/users/get_current_account |
フォルダ情報 |
/1/delta |
/2/files/list_folder |
ファイルダウンロード |
/1/files |
/2/files/download |
ファイルアップロード |
/1/files_put |
/2/files/upload |
エンドポイントを比較すると確かにやりたいことがわかりやすく変更されています
仕様変更による改善ポイント
- HTTPの使用を簡素化した(エンドポイントは常にHTTP POST)
- エラーに対するHTTPステータスコードの使用を簡素化
- デベロッパーへのAPI仕様の理解のしやすさを重視
- システムロジック、APIドキュメント、サンプルコードをまとめてSDKにラップすることで窓口をひとつにした
つまりRESTのデメリットとして上げた開発側の思想("優れたREST設計"理論)よりも、デベロッパー側のわかりやすさでリファレンスと向き合う時間を短縮させAPIユーザーの母数を増やす事を優先させた故の様です。
まとめ
当たり前のことですが、決してRESTはRPCの上位互換ではなく、APIアーキテクチャスタイルの違いなのでそれぞれ長所短所がある
RPCスタイルの適性
- RPCスタイルのエンドポイントは、1つのジョブだけをうまく実行したい場合に最適
- エンドポイントは1つのことしか実行しない
- サービス内にビジネスロジックを実装できるのでシンプルかつ明確になる
- 基本的なCRUD準拠のMVCシステムは、スケーリングの必要性がない限りはRPCの恩恵の方が大きい
- デベロッパー → サービスの様にサービスの機能や手法の一部を限定的に利用する場合にはこちらのスタイルが向いてる(Dropboxのストレージ一覧取得やslackのBOT通知など)
RESTスタイルの適性
- RESTスタイルはデータを要求するクライアントがいくつもある場合に役立つ
- 1ドメイン=1タスクのようにデータを別々のドメインに分離している
- データを要求するアプリやプラットフォームがいくつもある場合に役立つ
- 提供データをアプリケーションまたはビジネスロジックから切り離せる
- 分散型の大規模システムはRESTの恩恵を受けやすい
- サービス → サードパーティの様に分析からの可視化や拡張による付加価値を軸にマイクロサービスをスケールさせるコンシューマ指向のスタイル(twitterのトレンドやGoogleカレンダーのイベント情報など)
自身のサービスにとってのWEB APIの役割を見極める
オープンな情報をユーザーに気軽に提供する意味ではREST APIの設計理論は最適ですが、
それ故に柔軟な手段が時にはサービス(コアシステム)改善の足を引っ張って"本当に必要だったもの"から遠ざかってしまうし、
他にやらなきゃいけないことは山ほどあるので『WEB API導入=サービスの向上手段』という幻想に囚われすぎないのが大事
そもそも個人開発や小規模のサービスの段階で「RESTfulでスマートな開発&運用手段を」というのがローテク層が抱くAIみたいな幻想なのかもしれない・・・