The NEXT of REST

前後リンク

REST で解決していない問題

REST っていうのは本当に難しくて、

のような問題を抱えています。

これかを解決するために、API Query Language (GraphQL) や Backend for Frontend というものが生まれているのが 2016 年ぐらいからのトレンドです。

GraphQL

2012 : Facebook の mobile アプリの中で使われていた

2015 : OSS 化

2016 : GitHub が public API に採用

といった流れで一気に広がり、 現在では Shopify や Pinterest、NYTimes 等々、色んなところで採用されている技術です。 日本だと idobata が先行事例かな。

GraphQL とは

サンプル

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 TABLESSHOW COLUMNS FROM [table] を見て考えるような感じ。

IDE

型があるので、補完が作れます。 GraphiQL という IDE がとても綺麗に結合していて、クエリを書くときに補完も効くし その場で type 定義も見られるし、Cmd-Enter でクエリ実行できるし、めっちゃよく出来てる。

ドキュメントレスでも型定義と IDE さえあればなんとなくクエリを構築できるようになります。

GraphiQL

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 の優位点

REST の優位点

今移行すべきなのか

といった難所がまだ少し残っている感じ。

でももはや時期尚早ではなく、舗装されてはないが砂利道ぐらいの道路はあるので車で乗り付けることができるかなという感触です。

とにかく、Query Language であることには夢がある。

クライアントにクエリの責務が移り、サーバはそれに対して最速のレスポンスを 返すことだけに注力できると、API 職人は不要になり、DBA 的な役割になる。 (何か起きたときや不安を解消するときだけ出動する感じ)

これはサーバエンジニアが不要という mBaaS 的な世界観を意味していて、 当時の mBaaS はクライアント && ユーザに負荷を強いた (N+1 リクエストとか) ので廃れたんだけど 今なら「type だけ書いて、必要なところだけチューニング」という省力開発でサーバが作れるかもしれない。

一番のメリットは「クライアントが自分だけで改善できる」という精神的なものだと思う。 スキーマファーストでも、機能改善を行おうと思うとサーバの手を借りる必要があった。

Backend for Frontend

もう一つ「The NEXT of REST」として紹介したいのは BFF です。 Backend for Frontend で、サーバ側ではあるんだけどフロント寄りの、こういう層のこと。

What is BFF

https://speakerdeck.com/yosuke_furukawa/step-by-step-bff

マイクロサービスアーキテクチャ (原題: Building microservices) という本の中で 紹介されたこともあり、マイクロサービスの文脈でよく使われています。

実例

例えば、ランキングを表示するときに

クライアントは「ランキングをくれ」とリクエストを投げるだけで

ranking BFF

みたいな API Aggregation の機能を持ちます。

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 集約のツラみを一手に引き受けるので、 クライアント開発チームを大幅に拡充していく、フォローする仕組みを用意しておく等の 対応を入れておかないと失敗しやすいかなと考えています。

まとめ

前後リンク