Help us understand the problem. What is going on with this article?

Elixir: graphql-elixir を試す

More than 5 years have passed since last update.

概要

Elixir 用の GraphQL パーサーは awesome-graphql によると3つあります。

  1. asonge/graphql
  2. joshprice/graphql-elixir
  3. peburrows/plot

この中で開発が一番活発な graphql-elixir を調べました。

クエリーのパース

GraphQL のパースは次のように行います。

hello
GraphQL.parse "{ hello }"

まずは 以前の記事 で試した簡単なクエリーをパースしてみます。

{game {
  id
}}

次のように 仕様に定義されている要素 に分解されます。

[kind: :Document, loc: [start: 0],
 definitions: [[kind: :OperationDefinition, loc: [start: 0], operation: :query,
   selectionSet: [kind: :SelectionSet, loc: [start: 0],
    selections: [[kind: :Field, loc: [start: 0], name: 'game',
      selectionSet: [kind: :SelectionSet, loc: [start: 0],
       selections: [[kind: :Field, loc: [start: 0], name: 'id']]]]]]]]]

mutation も正しくパースできます。

mutation Mutation($input:CheckHidingSpotForTreasureInput!) {
  checkHidingSpotForTreasure(input:$input) {
    clientMutationId
  }
}
[kind: :Document, loc: [start: 0],
 definitions: [[kind: :OperationDefinition, loc: [start: 0],
   operation: :mutation, name: 'Mutation',
   variableDefinitions: [[kind: :VariableDefinition, loc: [start: 0],
     variable: [kind: :Variable, loc: [start: 0], name: 'input'],
     type: [kind: :NonNullType, loc: [start: 0],
      type: [kind: :NamedType, loc: [start: 0],
       name: 'CheckHidingSpotForTreasureInput']]]],
   selectionSet: [kind: :SelectionSet, loc: [start: 0],
    selections: [[kind: :Field, loc: [start: 0],
      name: 'checkHidingSpotForTreasure',
      arguments: [[kind: :Argument, loc: [start: 0], name: 'input',
        value: [kind: :Variable, loc: [start: 0], name: 'input']]],
      selectionSet: [kind: :SelectionSet, loc: [start: 0],
       selections: [[kind: :Field, loc: [start: 0],
         name: 'clientMutationId']]]]]]]]]

変数 $inputkind: :Variable としてパースできています。

変数への代入は後述の executor で実装されそうです。

他のパターンは graphql-elixirtest/graphql_parser_test.exs で確認できます。

schema のパース

schema のパースにも対応しており、こちらの サンプルnpm run update-schema したときに生成される data/schema.graphql では次のようになります。

data/schema.graphql
input CheckHidingSpotForTreasureInput {
  id: ID!
  clientMutationId: String!
}

type CheckHidingSpotForTreasurePayload {
  hidingSpot: HidingSpot
  game: Game
  clientMutationId: String!
}

<中略>

type Query {
  node(id: ID!): Node
  game: Game
}
[kind: :Document, loc: [start: 0],
 definitions: [[kind: :InputObjectTypeDefinition, loc: [start: 0],
   name: 'CheckHidingSpotForTreasureInput',
   fields: [[kind: :InputValueDefinition, loc: [start: 0], name: 'id',
     type: [kind: :NonNullType, loc: [start: 0],
      type: [kind: :NamedType, loc: [start: 0], name: 'ID']]],
    [kind: :InputValueDefinition, loc: [start: 0], name: 'clientMutationId',
     type: [kind: :NonNullType, loc: [start: 0],
      type: [kind: :NamedType, loc: [start: 0], name: 'String']]]]],
  [kind: :ObjectTypeDefinition, loc: [start: 0],
   name: 'CheckHidingSpotForTreasurePayload',
   fields: [[kind: :FieldDefinition, loc: [start: 0], name: 'hidingSpot',
     type: [kind: :NamedType, loc: [start: 0], name: 'HidingSpot']],
    [kind: :FieldDefinition, loc: [start: 0], name: 'game',
     type: [kind: :NamedType, loc: [start: 0], name: 'Game']],
    [kind: :FieldDefinition, loc: [start: 0], name: 'clientMutationId',
     type: [kind: :NonNullType, loc: [start: 0],
      type: [kind: :NamedType, loc: [start: 0], name: 'String']]]]],

<中略>

  [kind: :ObjectTypeDefinition, loc: [start: 0], name: 'Query',
   fields: [[kind: :FieldDefinition, loc: [start: 0], name: 'node',
     arguments: [[kind: :InputValueDefinition, loc: [start: 0], name: 'id',
       type: [kind: :NonNullType, loc: [start: 0],
        type: [kind: :NamedType, loc: [start: 0], name: 'ID']]]],
     type: [kind: :NamedType, loc: [start: 0], name: 'Node']],
    [kind: :FieldDefinition, loc: [start: 0], name: 'game',
     type: [kind: :NamedType, loc: [start: 0], name: 'Game']]]]]]

クエリーと schema の整合性チェックの機能は今のところ無いようです。

executor

schema に elixir の関数を設定しておくことで、クエリーので指定した変数を関数に渡すことできるようになるようです。
※ 最新の Version 0.0.2 と master ブランチでは仕様が変わっているので、まだ大きく変わるかもしれません。

defmodule TestSchema do
    def schema do
      %GraphQL.Schema{
        query: %GraphQL.ObjectType{
          name: "RootQueryType",
          fields: [
            %GraphQL.FieldDefinition{
              name: "greeting",
              type: "String",
              resolve: &greeting/1,
            }
          ]
        }
      }
    end

    def greeting(name: name), do: "Hello, #{name}!"
    def greeting(_), do: greeting(name: "world")
  end

  test "query arguments" do
    query = "{ greeting }"
    assert GraphQL.execute(TestSchema.schema, query) == [data: [greeting: "Hello, world!"]]

    query = "{ greeting(name: \"Elixir\") }"
    assert GraphQL.execute(TestSchema.schema, query) == [data: [greeting: "Hello, Elixir!"]]
  end

最後に

他の2つに比べてコミット数も多く最近も開発されていますが、簡単に使えるようになるにはもう少し時間がかかりそうです。

Plot も軽くさわってみましたが、次のような問題がありました。

  • variables があるとエラーになる
  • schema のパースでエラー
  • パラメータにオブジェクトを渡すとエラー (文字と数字はok)

こちらはさらに時間がかかりそうです。

サンプルコードはこちらです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away