LoginSignup
7
6

More than 5 years have passed since last update.

Elixir: graphql-elixir を試す

Last updated at Posted at 2015-11-05

概要

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)

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

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

7
6
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
7
6