absinthe v1.5.0-alpha.4 Absinthe.Schema View Source
Build GraphQL Schemas
Custom Schema Manipulation (in progress)
In Absinthe 1.5 schemas are built using the same process by which queries are
executed. All the fancy macros build up an intermediary tree of structs in the
%Absinthe.Blueprint{}
namespace, which we generally call “Blueprint structs”.
At the top you’ve got a %Blueprint{}
struct which holds onto some schema
definitions that look a bit like this:
%Blueprint.Schema.SchemaDefinition{
type_definitions: [
%Blueprint.Schema.ObjectTypeDefinition{identifier: :query, ...},
%Blueprint.Schema.ObjectTypeDefinition{identifier: :mutation, ...},
%Blueprint.Schema.ObjectTypeDefinition{identifier: :user, ...},
%Blueprint.Schema.EnumTypeDefinition{identifier: :sort_order, ...},
]
}
You can see what your schema’s blueprint looks like by calling
__absinthe_blueprint__
on any schema or type definition module.
defmodule MyAppWeb.Schema do
use Absinthe.Schema
query do
end
end
> MyAppWeb.Schema.__absinthe_blueprint__
#=> %Absinthe.Blueprint{...}
These blueprints are manipulated by phases, which validate and ultimately construct a schema. This pipeline of phases you can hook into like you do for queries.
defmodule MyAppWeb.Schema do
use Absinthe.Schema
@pipeline_modifier MyAppWeb.CustomSchemaPhase
query do
end
end
defmodule MyAppWeb.CustomSchemaPhase do
alias Absinthe.{Phase, Pipeline, Blueprint}
# Add this module to the pipeline of phases
# to run on the schema
def pipeline(pipeline) do
Pipeline.insert_after(pipeline, Phase.Schema.TypeImports, __MODULE__)
end
# Here's the blueprint of the schema, let's do whatever we want with it.
def run(blueprint, _) do
{:ok, blueprint}
end
end
The blueprint structs are pretty complex, but if you ever want to figure out how to construct something in blueprints you can always just create the thing in the normal AST and then look at the output. Let’s see what interfaces look like for example:
defmodule Foo do
use Absinthe.Schema.Notation
interface :named do
field :name, :string
end
end
Foo.__absinthe_blueprint__ #=> ...
Link to this section Summary
Functions
Get all concrete types for union, interface, or object
List all directives on a schema
List all implementors of an interface on a schema
Run the introspection query on a schema
Get all introspection types
Defines a root Mutation object
Defines a root Query object
Replace the default middleware
Defines a root Subscription object
List all types on a schema
Get all types that are used by an operation
Link to this section Types
Link to this section Functions
concrete_types(t(), Absinthe.Type.t()) :: [Absinthe.Type.t()]
Get all concrete types for union, interface, or object
directives(t()) :: [Absinthe.Type.Directive.t()]
List all directives on a schema
implementors(t(), Absinthe.Type.identifier_t() | Absinthe.Type.Interface.t()) :: [Absinthe.Type.Object.t()]
List all implementors of an interface on a schema
introspect(schema :: t(), opts :: Absinthe.run_opts()) :: Absinthe.run_result()
Run the introspection query on a schema.
Convenience function.
introspection_types(t()) :: [Absinthe.Type.t()]
Get all introspection types
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
Replace the default middleware
Examples
Replace the default for all fields with a string lookup instead of an atom lookup:
def middleware(middleware, field, object) do
new_middleware = {Absinthe.Middleware.MapGet, to_string(field.identifier)}
middleware
|> Absinthe.Schema.replace_default(new_middleware, field, object)
end
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 when 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 config
macro:
subscription do
field :new_users, :user do
arg :account_id, non_null(:id)
config fn args,_info ->
{:ok, topic: 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)
config fn args, _info ->
{:ok, topic: 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 config
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
Get all types that are used by an operation