$30 off During Our Annual Pro Sale. View Details »

Build REST API with GraphQL Ruby

Build REST API with GraphQL Ruby

Fumiaki MATSUSHIMA

August 28, 2020
Tweet

More Decks by Fumiaki MATSUSHIMA

Other Decks in Programming

Transcript

  1. @mtsmfm
    GraphQL Ruby で作る
    REST API

    View Slide

  2. @mtsmfm
    松島 史秋
    GraphQL Tokyo 主催者
    DbD と Ruby と GraphQL が好き
    主にバックエンドの開発

    View Slide

  3. 伝えたいこと
    - 宣言とクエリという考え方は、API サーバの実
    装をシンプルにする
    - GraphQL は勝手にそうなる
    - REST API にも応用できるかも
    - Ruby は実装例として出したけど Ruby 以外も考え方は
    同じはず

    View Slide

  4. REST API を作るときの課題
    - 似て非なるリソース
    - GET /articles
    - [{id: 1, title: “a”}, {id: 2, title: “b”}, ...]
    - GET /articles/:id
    - [{id: 1, title: “a”, thumbnail_url: “example.com”}]
    - thumbnail_urlという差分をどう表現するか

    View Slide

  5. 差分をどう表現するか
    1. 両方とも thumbnail_url を入れる
    class ArticleEntity < Entity; end
    def as_json
    {id: @article.id, title: @article.title, thumbnail_url: @article.thumbnail_url}
    end
    end
    get "/articles" do
    Article.all.map {|a| ArticleEntity.new(a) }.to_json
    end
    get "/articles/:id" do
    ArticleEntity.new(Article.find(params[:id]).to_json
    end

    View Slide

  6. 差分をどう表現するか
    1. 両方とも thumbnail_url を入れる
    a. 深く考えないと一覧での処理が重くなりがち
    b. あとで問題になったときに実は使ってないかわかり
    にくい
    i. Web、iOS、Android とクライアントすべてコード
    見ないと消せるかわからない

    View Slide

  7. 差分をどう表現するか
    2. /articles/:id だけ thumbnail_url を入れる
    class ArticleListEntity < ArticleCommonEntity; end
    class ArticleDetailEntity < ArticleCommonEntity;
    def as_json
    super.merge(thumbnail_url: @article.thumbnail_url)
    end
    end
    get "/articles" do
    Article.all.map {|a| ArticleListEntity.new(a) }.to_json
    end
    get "/articles/:id" do
    ArticleDetailEntity.new(Article.find(params[:id]).to_json
    end

    View Slide

  8. 差分をどう表現するか
    2. /articles/:id だけ thumbnail_url を入れる
    a. 同じところと違うところの表現が苦しくなりがち
    i. ArticleEntity、ArticleLiteEntity を
    ArticleCommonEntity から継承 or mixin
    ii. オプションで渡す
    別テーブルだと preload の必要性の有無も
    変わってくる

    View Slide

  9. GraphQL なら
    class ArticleType < GraphQL::Schema::Object
    field :id, ID, null: false
    field :title, String, null: false
    field :thumbnail_url, String, null: false
    end
    class QueryType < Types::BaseObject
    field :articles, [ArticleType], null: true
    field :article, ArticleType, null: true do
    argument :id, ID, required: true
    end
    def articles; Article.all; end
    def article(id:); Article.find(id); end
    end
    class AppSchema < GraphQL::Schema
    query QueryType
    end
    post "/graphql" do
    AppSchema.execute(
    Params[:query],
    Params[:variables]
    ).to_json
    end

    View Slide

  10. GraphQL なら
    - 宣言的に Type class を 1 つ書けば OK
    - クエリによって評価されたりされなかったりによ
    る区別
    => 宣言とクエリ
    - N+1 も Loader がクエリによって評価されたりさ
    れなかったりすることで、最低限の計算で解消

    View Slide

  11. GraphQL クライアント問題
    - 原理的には JSON を HTTP POST して、レス
    ポンスが parse できればなんでもいい
    - 型の恩恵を受けようとすると、特に native でク
    ライアントライブラリへのランタイム依存を持た
    ない codegen の類が (ほぼ) ない
    - 既存アプリへの導入の課題となり得る
    - codegen の作りの問題ではある

    View Slide

  12. GraphQL のメトリクスやログ問題
    - 既存の仕組みに乗っかれない
    - パスによる監視
    - HTTP method による監視
    - ログ
    - 最悪クエリを parse しないと分析できないことがあるか

    - BigQuery なら JS 動かせるのでなんでもあり
    - Operation 名つけとけばなんとかなりそうでは
    あるものの、既存資産の利用が困難

    View Slide

  13. Persisted Query
    - クエリを事前に登録しておく
    - クエリの形ごとに ID を振っておく
    - クライアントはクエリではなくクエリの ID を投げ
    つける

    View Slide

  14. Persisted Query
    post "/graphql" do
    AppSchema.execute(params[:query]).to_json
    end
    post "/graphql" do
    query = QueryStore.get(params[:query_id])
    AppSchema.execute(query).to_json
    end

    View Slide

  15. Path based Persisted Query
    - (勝手に命名)
    - クエリをエンドポイント毎ベタ書きする
    - クライアントは普通の REST API のように使う

    View Slide

  16. Path-based Persisted Query
    get "/articles" do
    AppSchema.execute(<<~GQL).to_json
    query {
    articles {
    id, title
    }
    }
    GQL
    end
    get "/articles/:id" do
    AppSchema.execute(<<~GQL, {id: params[:id]}).to_json
    query getArticle($id: ID!) {
    article(id: $id) {
    id, title, thumbnail_url
    }
    }
    GQL
    end

    View Slide

  17. ご清聴ありがとうございました
    - 宣言とクエリという考え方は、API サーバの実
    装をシンプルにする
    - GraphQL は勝手にそうなる
    - REST API にも応用できるかも
    - Ruby は実装例として出したけど Ruby 以外も考え方は
    同じはず

    View Slide