32
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Ruby で始める GraphQL Client

Last updated at Posted at 2020-05-27

これは何

  • Ruby のコードから GraphQL API を叩く方法を紹介します
    • Ruby のコードで GraphQL API を作る方法は紹介しません

GraphQL とは

GraphQL とは何か、を一言で表現するのは難しいのですが、個人的には「API を表現するプロトコルの一種」というような認識です。
REST API と比較されることが多いです。REST API で

curl http://example.com/users/123/posts/

のようにして叩くところを、GraphQL では

curl http://example.com/graphql -X POST --data '
  {
    user("123") {
      posts {
        title
      }
    }
  }
'

のように叩きます。REST API では /users/ /users/123/ /users/123/posts/ のように様々な URL にリクエストを投げて目的の情報を手に入れますが、GraphQL では 1 つのエンドポイントに様々なクエリを投げ分けることで、必要な情報を全て(不必要な情報は除いて)1 度のリクエストで取得することができます。

GraphQL API の利用方法を学ぶ

取り急ぎ GraphQL API を利用することができればよかったので、私は GraphQL 公式ガイド (https://graphql.org/learn/) の Queries and MutationsSchemas and Types だけを読みました。やや長めのページ 2 つを読むだけで概要が理解できたのでおすすめです。

ただし注意点が 1 つあります。 例として登場する API の意味を深く考えてはいけません。 例えば、冒頭でこんなクエリが登場します。

{
  hero {
    name
  }
}

これに対して、サンプル API はこんなレスポンスを返しています。

{
  "data": {
    "hero": {
      "name": "R2-D2"
    }
  }
}

この例を見たとき、「hero_id を指定したわけでもないのになぜ R2-D2 を返すんだろう?」「hero_id を指定しないときは hero 全てを返す方がいいのでは?」というのが気になって、全体の理解の妨げになってしまいました。実際にはこの API はただのサンプルでしかないので、実用性などは無視して「そういうもの」と思い込む必要があります。単に「任意引数として Episode を 1 つ受け取り、Character を 1 つ返す、hero という Query」があるだけです。

学習用 GraphQL サーバーを立てる

上述した GraphQL 公式ガイドに出てくる謎のスターウォーズ API を再現したものが apollographql/starwars-server です。このあと Ruby から叩くのに使うので、さくっと起動しておきましょう。ブラウザから http://localhost:8080/graphql にアクセスできれば起動成功です。

なお、似た名前のものに graphql/swapi-graphql がありますが、これはより本格的なスターウォーズ API の GraphQL ラッパーなので別物です。気をつけましょう。

Ruby で GraphQL クライアントを使ってみる

Ruby 製の GraphQL Client として github/graphql-client があります。github がオーナーという安心感が凄かったので、ひとまずこれを使おうと思います。

README にも書かれていますが、以下のようにして使います。

require "graphql/client"
require "graphql/client/http"

module SWAPI
  # http アダプターを設定
  HTTP = GraphQL::Client::HTTP.new("http://localhost:8080/graphql")

  # 上記を使って API サーバーから GraphQL Schema 情報を取得
  Schema = GraphQL::Client.load_schema(HTTP)

  # 上記を使ってクライアントを作成
  Client = GraphQL::Client.new(schema: Schema, execute: HTTP)
end

HeroQuery = SWAPI::Client.parse <<~'GRAPHQL'
 {
  hero {
    name
  }
}
GRAPHQL

result = SWAPI::Client.query(HeroQuery)
pp result.to_h

上記では Schema 情報をサーバーから取得していますが、以下のようにしてあらかじめローカルに保存しておき、それをロードする方法もあるそうです。


module SWAPI
  # http アダプターを設定(上例と同じ)
  HTTP = GraphQL::Client::HTTP.new("http://localhost:8080/graphql")

  # ローカルファイルから GraphQL Schema 情報を取得
  Schema = GraphQL::Client.load_schema("path/to/schema.json")

  # 上記を使ってクライアントを作成(上例と同じ)
  Client = GraphQL::Client.new(schema: Schema, execute: HTTP)
end

# API サーバーから GraphQL Schema 情報を取得してローカルファイルに出力
GraphQL::Client.dump_schema(SWAPI::HTTP, "path/to/schema.json")

# ...

注意点

クエリに Operation Name を付ける場合、 SWAPI::Client.query() の引数にモジュール名のように付けて渡す必要があります。

HeroQuery = SWAPI::Client.parse <<~'GRAPHQL'
 # NameOfHero という名前をつけた場合・・・
 query NameOfHero {
  hero {
    name
  }
}
GRAPHQL

# HeroQuery ではなく HeroQuery::NameOfHero を渡す必要がある!
result = SWAPI::Client.query(HeroQuery::NameOfHero)

これを忘れると、 expected definition to be a GraphQL::Client::OperationDefinition but was GraphQL::Language::Nodes::Document (TypeError) というエラーが起きます。

またこのため、必然的に小文字から始まる名前も付けられません(client.rb:241:in 'const_set': wrong constant name ... (NameError) というエラーが起きます)。

この ↓ あたりの issue は全て単にこの仕様を見落としている気がします。

README に明記されていないので私もハマってしまいました・・・(コントリビュートチャンス?)

32
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?