absinthe v0.2.2 Absinthe.Schema behaviour
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).
Summary
Functions
Verify that a schema is correctly formed
Callbacks
(Optional) Define the mutation root type
(Required) Define the query root type
(Optional) Define the subscription root type
Types
t :: %{query: Absinthe.Type.Object.t, mutation: nil | Absinthe.Type.Object.t, subscription: nil | Absinthe.Type.Object.t, type_modules: [atom], types: Absinthe.Schema.Types.typemap_t, errors: [binary]}
A struct containing the defined schema details.
Don’t create these structs yourself. Just define
the necessary Absinthe.Schema
callbacks and
use schema/0
Functions
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)
Callbacks
Specs
mutation :: nil | Absinthe.Type.Object.t
(Optional) Define the mutation root type.
Should be an Absinthe.Type.Object
struct.
Specs
query :: Absinthe.Type.Object.t
(Required) Define the query root type.
Should be an Absinthe.Type.Object
struct.
Specs
subscription :: nil | Absinthe.Type.Object.t
(Optional) Define the subscription root type.
Should be an Absinthe.Type.Object
struct.