The NEXT of REST
前後リンク
- RESTful API のおさらい
- Rails での JSON API 実装まとめ
- スキーマファースト開発
- The NEXT of REST
REST で解決していない問題
REST っていうのは本当に難しくて、
- 開発者のお気持ちで API が異なる
- 指針であって仕様じゃないのが理由
- POST がワイルドカードとして使われるとか
- クライアントとサーバ間のまだある精神的な溝
- API はサーバが作るもので、クライアントは手出ししづらいという意識
- REST だとクライアントごとに最適化した API を作りづらい
- Web とスマホで同じ API を使うときに不要なレスポンスがある
- 提供されている API が不十分なときにクライアント側で JOIN するハメに
のような問題を抱えています。
これかを解決するために、API Query Language (GraphQL) や Backend for Frontend というものが生まれているのが 2016 年ぐらいからのトレンドです。
GraphQL
2012 : Facebook の mobile アプリの中で使われていた
2015 : OSS 化
2016 : GitHub が public API に採用
といった流れで一気に広がり、 現在では Shopify や Pinterest、NYTimes 等々、色んなところで採用されている技術です。 日本だと idobata が先行事例かな。
GraphQL とは
- API Query Language
- Query Language なので SQL と対比させると分かりやすいと思う
- 「こんなレスポンスをくれ」と書くと、その通りのレスポンスが返ってくる
- ただ一つの (POST の) エンドポイントを持ちます。
- HTTP メソッドの使い分け等は行いません。ただ GraphQL のクエリを受け付けるのみ
- レスポンスも 200 OK でエラーを返します
- REST ではない RPC 的な考え方
サンプル
query:
{
user(id: "onk") {
id,
name,
avatar(size: 50) {
uri,
width,
height
}
}
}
response:
{
"user": {
"id": "onk",
"name": "Takafumi ONAKA",
"avatar": {
"uri": "http://example.com/pic.jpg",
"width": 50,
"height": 50
}
}
}
このように、クエリに書いた通りのデータを受け取れていることが分かるでしょうか?
Query の説明
返すフィールドを Query で指定可能
{
user(id: "onk") {
id, # <- 返すフィールドを指定可能
name, # <-
avatar(size: 50) {
uri, # <-
width, # <-
height # <-
}
}
}
データのフェッチ条件を指定可能
{
user(id: "onk") { # <- データのフェッチ条件を指定可能
id,
name,
avatar(size: 50) { # <-
uri,
width,
height
}
}
}
関連するオブジェクトを一発で取ってこれる
{
user(id: "onk") {
id,
name,
avatar(size: 50) { # <- 関連オブジェクトを取得
uri, # <-
width, # <-
height # <-
} # <-
}
}
とかです。 Query っぽいですね。
その他のクエリ
いわゆる GET っぽい Query と、 POST/PUT/DELETE っぽい Mutation と、 あと Pub/Sub のための Subscribe があります。
型の書き方
Types::UserType = GraphQL::ObjectType.define do
name "User"
field :id, !types.ID
field :name, types.String
field :avatar, Types::PhotoType
connection :articles, Types::Article.connection_type do
resolve ->(user, args, context) do
user.articles
end
end
end
どんな Type があり、その Type ではどんな field や connection が取得可能なのかをカッチリ書きます。
この型を公開していて、クライアントはこの型をドキュメント代わりにクエリを構築します。
ちょうど SQL を書くときに SHOW TABLES
や SHOW COLUMNS FROM [table]
を見て考えるような感じ。
IDE
型があるので、補完が作れます。 GraphiQL という IDE がとても綺麗に結合していて、クエリを書くときに補完も効くし その場で type 定義も見られるし、Cmd-Enter でクエリ実行できるし、めっちゃよく出来てる。
ドキュメントレスでも型定義と IDE さえあればなんとなくクエリを構築できるようになります。
GraphQL vs ...
vs REST
REST | GraphQL | |
---|---|---|
何が返ってくるか | 開発者のお気持ち | WISIWYG |
色々データが必要 | N+1 Request | 1 Request |
型システム | 無い | 組み込まれている |
ドキュメント | 無い | 型があるし、レスポンスはドキュメント不要 |
ページネート | 開発者のお気持ち | 組み込まれている |
endpoint | たくさん | 1つ |
多様なクライアント | API を量産して対応 | Query として必要なものを書ける |
と、「何を投げたら何が返ってくるか分かる」「field ごとに型がある」とメリットが多そうに見えるんだけど
vs スキーマファースト
スキーマファースト開発 で話した内容との比較だと
REST | GraphQL | |
---|---|---|
何が返ってくるか | Spec に書いてある | WISIWYG |
色々データが必要 | N+1 Request | 1 Request |
型システム | Spec に書いてある | 組み込まれている |
ドキュメント | Spec から自動生成 | 型があるし、レスポンスはドキュメント不要 |
ページネート | 開発者のお気持ち | 組み込まれている |
endpoint | たくさん | 1つ |
多様なクライアント | API を量産して対応 | Query として必要なものを書ける |
vs 職人的スキーマファースト
ここに、「API 職人」と呼ばれる類いの人が存在するとこうなります。
REST | GraphQL | |
---|---|---|
何が返ってくるか | Spec に書いてある | WISIWYG |
色々データが必要 | 職人のおかげで一撃 | 1 Request |
型システム | Spec に書いてある | 組み込まれている |
ドキュメント | Spec から自動生成 | 型があるし、レスポンスはドキュメント不要 |
ページネート | 職人が統一して Specに反映 | 組み込まれている |
endpoint | たくさん | 1つ |
多様なクライアント | 職人がオプション等で対応 | Query として必要なものを書ける |
例えば「/mypage」というエンドポイントを作って、マイページの表示に必要なリソースを全て返すとか、 ページネート方法を綺麗に統一するとか、 返すフィールドをクエリパラメータ等で指定できるようにするとかですね。
というわけで、職人が居る環境なら、GraphQL で解決する問題はスキーマでも解決しています。 真面目にスキーマファーストで開発できているなら、ここに挙げたような観点ではほとんど優位性はありません。
GraphQL まとめ
GraphQL の優位点
- API の改善が進みやすくなる
- クエリがあるので、どのクライアントがどういう情報を必要としているかを把握できる
- REST API ではたどり着きづらい「必要最低限のレスポンス」に到達できる
- クエリ構築の責務をクライアント側に持って行ける
- クエリという関心事がクライアントに移動する
- クライアント側が改善サイクルの中心にあるので、自由度が高い方が良い
- API 職人不要の世界観に
- サーバ側が爆速で構築できる可能性
- Query Language
- リクエストを受けた瞬間にクエリをパースできる
- そのリクエストを処理可能かどうかをクエリから判断できる
- ACL 周りのミスが激減する
REST の優位点
- 15 年の歳月
- 培った設計方針はほどよく枯れている
- Web 標準との親和性
- POST/PUT/DELETE の使い分け
- エラー概要がステータスコードで分かる
今移行すべきなのか
- サーバ側でクエリを上手にやりくりするノウハウが一般に広まっていない
- graphql-batch でできると言うものの
- エコシステムが快適に開発するにはまだ足りない
- 僕らは普段 SQL を生で書かずに ORM (ActiveRecord) を通しているので、 GraphQL においてもそのような何かが発生しそうな予感がある
- REST の 15 年の歴史に対抗するほど整っていない
- logging
- metrics 監視
といった難所がまだ少し残っている感じ。
でももはや時期尚早ではなく、舗装されてはないが砂利道ぐらいの道路はあるので車で乗り付けることができるかなという感触です。
とにかく、Query Language であることには夢がある。
クライアントにクエリの責務が移り、サーバはそれに対して最速のレスポンスを 返すことだけに注力できると、API 職人は不要になり、DBA 的な役割になる。 (何か起きたときや不安を解消するときだけ出動する感じ)
これはサーバエンジニアが不要という mBaaS 的な世界観を意味していて、 当時の mBaaS はクライアント && ユーザに負荷を強いた (N+1 リクエストとか) ので廃れたんだけど 今なら「type だけ書いて、必要なところだけチューニング」という省力開発でサーバが作れるかもしれない。
一番のメリットは「クライアントが自分だけで改善できる」という精神的なものだと思う。 スキーマファーストでも、機能改善を行おうと思うとサーバの手を借りる必要があった。
Backend for Frontend
もう一つ「The NEXT of REST」として紹介したいのは BFF です。 Backend for Frontend で、サーバ側ではあるんだけどフロント寄りの、こういう層のこと。
https://speakerdeck.com/yosuke_furukawa/step-by-step-bff
マイクロサービスアーキテクチャ (原題: Building microservices) という本の中で 紹介されたこともあり、マイクロサービスの文脈でよく使われています。
実例
例えば、ランキングを表示するときに
クライアントは「ランキングをくれ」とリクエストを投げるだけで
みたいな API Aggregation の機能を持ちます。
BFF に求められること、BFF のメリット
- API Aggregation
- フロントからのリクエストを上手に BFF が集約してくれるので、 バックエンドは REST を突き詰めてシンプルな設計にできます。
- フロントエンドは複数 API を叩いて自分で JOIN したり、 複数 API を並行に投げたりする責務から解放され、UI だけに注力できます。
- 認証
- フロントからのリクエストをすべて BFF が受けてバックエンドに流す場合、 バックエンドは認証から解放されます。
- クライアント最適化
- フロントエンドごとに、最適なレスポンスに BFF が加工して返すことができます。
BFF vs スキーマ
BFF は名前の通り「フロントエンドの事情に合わせたレスポンスを返す層」なので 特にスキーマ開発とは競合しません。直行した概念です。
バックエンドは大抵 RESTful API なので、スキーマ開発をしない理由が無い。 BFF もスキーマを書かない理由があまりありません。
クライアント側では、一撃で良い感じのデータが取得できるので 職人の手による作り込まれた API 開発と違いがありません。
REST | Backend for Frontend | |
---|---|---|
何が返ってくるか | Spec に書いてある | BFF で制御可能 |
色々データが必要 | 職人のおかげで一撃 | BFF で制御可能 |
型システム | Spec に書いてある | BFF で制御可能 |
ドキュメント | Spec から自動生成 | BFF で制御可能 |
ページネート | 職人が統一して Specに反映 | BFF で制御可能 |
endpoint | たくさん | 必要なだけ |
多様なクライアント | 職人がオプション等で対応 | クライアントごとに BFF を用意する |
BFF はクライアントの事情と密結合した層に「ツラみ」を集約するという発想なので、 クライアント種別ごとに BFF を立てるのが望ましいと考えています。 おそらくクライアントを作っている人が BFF も一緒に開発していく流れにすると自然でしょう。
このとき、サーバは綺麗な世界で開発できるのに対して、 クライアントは UI のツラみ、API 集約のツラみを一手に引き受けるので、 クライアント開発チームを大幅に拡充していく、フォローする仕組みを用意しておく等の 対応を入れておかないと失敗しやすいかなと考えています。
まとめ
- ビジネスにおいて、(ネイティブ or Web SPA) クライアントの UX が必須な時代なので、 API 開発の手綱もサーバからクライアントに移していくような流れが生まれています
- GraphQL も BFF も、クライアント側で制御していこうという発想
- 新しい概念「クエリ」を入れたのが GraphQL。クライアントはともかく、サーバは新規開発になる
- クライアントごとの中間レイヤーを作ろうというのが BFF。途中からでも導入しやすい
- スキーマ開発が上手くハマってないなぁという時に考えてみても良いかもしれない
- 組織構成上の理由が大きく影響すると思う
前後リンク
- RESTful API のおさらい
- Rails での JSON API 実装まとめ
- スキーマファースト開発
- The NEXT of REST