absinthe v1.4.0-beta.1 Absinthe.Schema behaviour View Source
Define a GraphQL schema.
See also Absinthe.Schema.Notation
for a reference of the macros imported by
this module available to build types for your 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
(and optionally, mutation
and subscription
).
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}
}
query do
@desc "Get an item by ID"
field :item, :item do
@desc "The ID of the item"
arg :id, type: :id
resolve fn %{id: id}, _ ->
{:ok, Map.get(@fake_db, id)}
end
end
end
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.
@desc "A valuable Item"
object :item do
field :id, :id
@desc "The item's name"
field :name, :string,
field :value, :integer, description: "Recently appraised value"
end
We can also load types from other modules using the import_types
macro:
defmodule App.Schema do
use Absinthe.Schema
import_types App.Schema.Scalars
import_types App.Schema.Objects
# ... schema definition
end
Our :item
type above could then move into App.Schema.Objects
:
defmodule App.Schema.Objects do
use Absinthe.Schema.Notation
object :item do
# ... type definition
end
# ... other objects!
end
Default Resolver
By default, if a resolve
function is not provided for a field, Absinthe
will attempt to extract the value of the field using Map.get/2
with the
(atom) name of the field.
You can change this behavior by setting your own custom default resolve
function in your schema. For example, given we have a field, name
:
field :name, :string
And we’re trying to extract values from a horrible backend API that gives us maps with uppercase (!) string keys:
%{"NAME" => "A name"}
Here’s how we could set our custom resolver to expect those keys:
default_resolve fn
_, %{source: source, definition: %{name: name}} when is_map(source) ->
{:ok, Map.get(source, String.upcase(name))}
_, _ ->
{:ok, nil}
end
Note this will now act as the default resolver for all fields in our schema
without their own resolve
function.
Link to this section Summary
Functions
Get all concrete types for union, interface, or object
List all directives on a schema
Return the default middleware set for a field if none exists
List all implementors of an interface on a schema
Lookup a directive
Lookup a type by name, identifier, or by unwrapping
Defines a root Mutation object
Defines a root Query object
Defines a root Subscription object
List all types on a schema
Link to this section Types
A module defining a schema.
Link to this section Functions
concrete_types(t, Absinthe.Type.t) :: [Absinthe.Type.t]
Get all concrete types for union, interface, or object
List all directives on a schema
Return the default middleware set for a field if none exists
implementors(t, Absinthe.Type.identifier_t | Absinthe.Type.Interface.t) :: [Absinthe.Type.Object.t]
List all implementors of an interface on a schema
lookup_directive(t, atom | binary) :: Absinthe.Type.Directive.t | nil
Lookup a directive.
lookup_type(atom, Absinthe.Type.wrapping_t | Absinthe.Type.t | Absinthe.Type.identifier_t, Keyword.t) :: Absinthe.Type.t | nil
Lookup a type by name, identifier, or by unwrapping.
Defines a root Mutation object
mutation do
field :create_user, :user do
arg :name, non_nulL(:string)
arg :email, non_nulL(:string)
resolve &MyApp.Web.BlogResolvers.create_user/2
end
end
Defines a root Query object
Defines a root Subscription object
Subscriptions in GraphQL let a client submit a document to the server that outlines what data they want to receive in the event of particular updates.
For a full walk through of how to setup your project with subscriptions and Phoenix see the Absinthe.Phoenix project moduledoc.
When you push a mutation, you can have selections on that mutation result to get back data you need, IE
mutation {
createUser(accountId: 1, name: "bob") {
id
account { name}
}
}
However, what if you want to know whe OTHER people create a new user, so that your UI can update as well. This is the point of subscriptions.
subscription {
newUsers {
id
account { name}
}
}
The job of the subscription macros then is to give you the tools to connect
subscription documents with the values that will drive them. In the last example
we would get all users for all accounts, but you could imagine wanting just
newUsers(accountId: 2)
.
In your schema you articulate the interests of a subscription via the topic
macro:
subscription do
field :new_users, :user do
arg :account_id, non_null(:id)
topic fn args ->
args.account_id
end
end
end
The topic can be any term. You can broadcast a value manually to this subscription by doing
Absinthe.Subscription.publish(pubsub, user, [new_users: user.account_id])
It’s pretty common to want to associate particular mutations as the triggers for one or more subscriptions, so Absinthe provides some macros to help with that too.
subscription do
field :new_users, :user do
arg :account_id, non_null(:id)
topic fn args ->
args.account_id
end
trigger :create_user, topic: fn user ->
user.account_id
end
end
end
The idea with a trigger is that it takes either a single mutation :create_user
or a list of mutations [:create_user, :blah_user, ...]
and a topic function.
This function returns a value that is used to lookup documents on the basis of
the topic they returned from the topic
macro.
Note that a subscription field can have trigger
as many trigger blocks as you
need, in the event that different groups of mutations return different results
that require different topic functions.
List all types on a schema