Current section

Files

Jump to
graphql lib graphql.ex
Raw

lib/graphql.ex

defmodule GraphQL do
@moduledoc ~S"""
The main GraphQL module.
The `GraphQL` module provides a
[GraphQL](http://facebook.github.io/graphql/) implementation for Elixir.
## Parse a query
Parse a GraphQL query
iex> GraphQL.parse "{ hello }"
{:ok, %{definitions: [
%{kind: :OperationDefinition, loc: %{start: 0},
operation: :query,
selectionSet: %{kind: :SelectionSet, loc: %{start: 0},
selections: [
%{kind: :Field, loc: %{start: 0}, name: "hello"}
]
}}
],
kind: :Document, loc: %{start: 0}
}}
## Execute a query
Execute a GraphQL query against a given schema / datastore.
# iex> GraphQL.execute schema, "{ hello }"
# {:ok, %{hello: "world"}}
"""
alias GraphQL.Schema
alias GraphQL.SyntaxError
defmodule ObjectType do
defstruct name: "RootQueryType", description: "", fields: []
end
defmodule FieldDefinition do
defstruct name: nil, type: "String", resolve: nil
end
@doc """
Tokenize the input string into a stream of tokens.
iex> GraphQL.tokenize("{ hello }")
[{ :"{", 1 }, { :name, 1, 'hello' }, { :"}", 1 }]
"""
def tokenize(input_string) when is_binary(input_string) do
input_string |> to_char_list |> tokenize
end
def tokenize(input_string) do
{:ok, tokens, _} = :graphql_lexer.string input_string
tokens
end
@doc """
Parse the input string into a Document AST.
iex> GraphQL.parse("{ hello }")
{:ok,
%{definitions: [
%{kind: :OperationDefinition, loc: %{start: 0},
operation: :query,
selectionSet: %{kind: :SelectionSet, loc: %{start: 0},
selections: [
%{kind: :Field, loc: %{start: 0}, name: "hello"}
]
}}
],
kind: :Document, loc: %{start: 0}
}
}
"""
def parse(input_string) when is_binary(input_string) do
input_string |> to_char_list |> parse
end
def parse(input_string) do
case input_string |> tokenize |> :graphql_parser.parse do
{:ok, parse_result} ->
{:ok, parse_result}
{:error, {line_number, _, errors}} ->
{:error, %{errors: [%{message: "GraphQL: #{errors} on line #{line_number}", line_number: line_number}]}}
end
end
@doc """
Execute a query against a schema.
# iex> GraphQL.execute(schema, "{ hello }")
# {:ok, %{hello: world}}
"""
def execute(schema, query) do
case parse(query) do
{:ok, document} ->
query_fields = hd(document[:definitions])[:selectionSet][:selections]
%Schema{
query: _query_root = %ObjectType{
name: "RootQueryType",
fields: fields
}
} = schema
result = for fd <- fields, qf <- query_fields, qf[:name] == fd.name do
arguments = Map.get(qf, :arguments, [])
|> Enum.map(&parse_argument/1)
{String.to_atom(fd.name), fd.resolve.(arguments)}
end
{:ok, Enum.into(result, %{})}
{:error, error} -> {:error, error}
end
end
defp parse_argument(%{kind: :Argument, loc: _, name: name, value: %{kind: _, loc: _, value: value}}) do
{String.to_atom(name), value}
end
end