概要
Elixir 用の GraphQL パーサーは awesome-graphql によると3つあります。
この中で開発が一番活発な graphql-elixir を調べました。
クエリーのパース
GraphQL のパースは次のように行います。
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']]]]]]]]]
変数 $input
も kind: :Variable
としてパースできています。
変数への代入は後述の executor で実装されそうです。
他のパターンは graphql-elixir
の test/graphql_parser_test.exs
で確認できます。
schema のパース
schema のパースにも対応しており、こちらの サンプル で npm run update-schema
したときに生成される 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)
こちらはさらに時間がかかりそうです。
サンプルコードはこちらです。