Packages
absinthe
0.2.1
1.11.0
1.10.2
1.10.1
1.10.0
1.9.1
1.9.0
1.8.0
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.8
1.6.7
retired
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.5
1.5.0-rc.4
1.5.0-rc.3
1.5.0-rc.2
1.5.0-rc.1
1.5.0-rc.0
1.5.0-beta.2
1.5.0-beta.1
1.5.0-beta.0
1.5.0-alpha.4
1.5.0-alpha.3
1.5.0-alpha.2
1.5.0-alpha.1
1.5.0-alpha.0
1.4.16
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.4.9
1.4.8
retired
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.3
1.4.0-rc.2
1.4.0-rc.1
1.4.0-rc.0
1.4.0-beta.5
1.4.0-beta.4
1.4.0-beta.3
1.4.0-beta.2
1.4.0-beta.1
1.3.2
1.3.1
1.3.0
1.3.0-rc.0
1.3.0-beta.2
1.3.0-beta.1
1.3.0-beta.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.0
1.2.0-beta.0
1.2.0-alpha0
1.2.0-alpha.2
1.2.0-alpha.1
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
0.5.2
0.5.1
0.5.0
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.2.3
0.2.2
0.2.1
0.1.0
GraphQL for Elixir
Current section
Files
Jump to
Current section
Files
lib/absinthe/schema.ex
defmodule Absinthe.Schema do
@moduledoc """
Define a GraphQL schema.
## Basic Usage
To define a schema, `use Absinthe.Schema` within
a module. This marks your module as adhering to the
`Absinthe.Schema` behaviour, and sets up some macros
and utility functions for your use:
```
defmodule App.Schema do
use Absinthe.Schema
# ... define it here!
end
```
Now, define a `query` function (and optionally, `mutation`
and `subscription`) functions. These should return the _root_
objects for each of those operations.
We'll define a `query` that has one field, `item`, to support
querying for an item record by its ID:
```
# Just for the example. You're probably using Ecto or
# something much more interesting than a module attribute-based
# database!
@fake_db %{
"foo" => %{id: "foo", name: "Foo", value: 4},
"bar" => %{id: "bar", name: "Bar", value: 5}
}
def query do
%Absinthe.Type.Object{
fields: fields(
item: [
type: :item,
description: "Get an item by ID",
args: args(
id: [type: :id, description: "The ID of the item"]
),
resolve: fn %{id: id}, _ ->
{:ok, Map.get(@fake_db, id)}
end
]
)
}
end
```
We use `Absinthe.Type.Definitions.fields/1` and
`Absinthe.Type.Definitions.args/1` here, available automatically because
of the `use Absinthe.Schema` at the top of our module. These are
convenience functions that ease compact, readable definitions for fields
and arguments.
For more information on object types (especially how the `resolve`
function works above), see `Absinthe.Type.Object`.
You may also notice we've declared that the resolved value of the field
to be of `type: :item`. We now need to define exactly what an `:item` is,
and what fields it contains.
Thankfully another `Absinthe.Type.Definitions` utility can be used to do this
easily, and inside our schema module. We just need to set the `@absinthe`
module attribute before a function that returns an object type:
```
@absinthe :type
def item do
%Absinthe.Type.Object{
description: "A valuable item",
fields: fields(
id: [type: :id],
name: [type: :string, description: "The item's name"],
value: [type: :integer, description: "Recently appraised value"]
)
}
end
```
(You can read more about building custom types and the available
convenience functions in `Absinthe.Type.Definitions` --
and check out `Absinthe.Type.Scalar`, where the built-in types like
`:integer`, `:id`, and `:string` are defined.)
We can also load types from other modules using the `:type_modules`
option in our `use Absinthe.Schema`, eg:
```
defmodule App.Schema do
use Absinthe.Schema, type_modules: [App.Schema.Scalars, App.Schema.Objects]
# ... schema definition
end
```
Our `:item` type above could then move into `App.Schema.Objects`:
```
defmodule App.Schema.Objects do
use Absinthe.Type.Definitions
@absinthe :type
def item do
# ... type definition
end
# ... other objects!
end
```
Our schema is now ready to be executed (using, eg, `Absinthe.run/2` and
friends).
"""
defmacro __using__(options) do
type_modules = options |> Keyword.get(:type_modules, [])
quote do
@behaviour unquote(__MODULE__)
def mutation, do: nil
def subscription, do: nil
defoverridable [mutation: 0,
subscription: 0]
@doc """
Build the configured schema struct.
This uses functions implementing the callbacks for
the `Abinthe.Schema` behaviour. You should never
need to create a schema struct manually.
"""
def schema do
contents = [
query: query,
mutation: mutation,
subscription: subscription
]
|> Enum.filter(fn
{_, nil} -> false
other -> true
end)
|> Enum.into(%{})
|> Map.merge(%{type_modules: [__MODULE__] ++ unquote(type_modules)})
struct(unquote(__MODULE__), contents)
|> unquote(__MODULE__).prepare
end
use Absinthe.Type.Definitions
end
end
@doc """
(Required) Define the query root type.
Should be an `Absinthe.Type.Object` struct.
"""
@callback query :: Absinthe.Type.Object.t
@doc """
(Optional) Define the mutation root type.
Should be an `Absinthe.Type.Object` struct.
"""
@callback mutation :: nil | Absinthe.Type.Object.t
@doc """
(Optional) Define the subscription root type.
Should be an `Absinthe.Type.Object` struct.
"""
@callback subscription :: nil | Absinthe.Type.Object.t
alias Absinthe.Type
alias Absinthe.Language
alias __MODULE__
@typedoc """
A struct containing the defined schema details.
Don't create these structs yourself. Just define
the necessary `Absinthe.Schema` callbacks and
use `schema/0`
"""
@type t :: %{query: Absinthe.Type.Object.t,
mutation: nil | Absinthe.Type.Object.t,
subscription: nil | Absinthe.Type.Object.t,
type_modules: [atom],
types: Schema.Types.typemap_t,
errors: [binary]}
defstruct query: nil, mutation: nil, subscription: nil, type_modules: [], types: %{}, errors: []
# Add types (but only do it once; if any have been found, this is just an identity function)
@doc false
@spec prepare(t) :: t
def prepare(%{types: types} = schema) when map_size(types) == 0 do
schema
|> Schema.Types.setup
end
def prepare(schema) do
schema
end
# Lookup a type that in used by/available to a schema
@doc false
@spec lookup_type(t, Type.wrapping_t | Type.t | Type.identifier_t) :: Type.t | nil
def lookup_type(schema, type) when is_map(type) do
if Type.wrapped?(type) do
lookup_type(schema, type |> Type.unwrap)
else
type
end
end
def lookup_type(schema, identifier) do
schema.types[identifier]
end
@doc false
@spec type_from_ast(t, Language.type_reference_t) :: Absinthe.Type.t | nil
def type_from_ast(schema, %Language.NonNullType{type: inner_type}) do
case type_from_ast(schema, inner_type) do
nil -> nil
type -> %Type.NonNull{of_type: type}
end
end
def type_from_ast(schema, %Language.ListType{type: inner_type}) do
case type_from_ast(schema, inner_type) do
nil -> nil
type -> %Type.List{of_type: type}
end
end
def type_from_ast(schema, ast_type) do
schema.types
|> Map.values
|> Enum.find(:name, fn
%{name: name} ->
name == ast_type.name
end)
end
@doc """
Verify that a schema is correctly formed
## Examples
`{:ok, schema}` is returned if the schema is free of errors:
```
{:ok, schema} = Absinthe.Schema.verify(App.GoodSchema)
```
Otherwise the errors are returned in a tuple:
```
{:error, errors} = Absinthe.Schema.verify(App.BadSchema)
```
"""
@spec verify(atom | t) :: {:ok, t} | {:error, [binary]}
def verify(name) when is_atom(name) do
verify(name.schema)
end
def verify(%Schema{errors: errors} = schema) when length(errors) > 0 do
{:error, errors}
end
def verify(%Schema{} = schema) do
{:ok, schema}
end
defimpl Absinthe.Traversal.Node do
def children(node, _) do
[node.query, node.mutation, node.subscription]
|> Enum.reject(&is_nil/1)
end
end
end