absinthe v1.4.12 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: non_null(: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

Link to this section Summary

Types

t()

A module defining a schema

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

Lookup a directive

Lookup a type by name, identifier, or by unwrapping

List all types on a schema

Get all types that are used by an operation

Callbacks

List of Plugins to run before / after resolution

Link to this section Types

A module defining a schema.

Link to this section Functions

Link to this function concrete_types(schema, type) View Source
concrete_types(t(), Absinthe.Type.t()) :: [Absinthe.Type.t()]

Get all concrete types for union, interface, or object

List all directives on a schema

Link to this function implementors(schema, ident) View Source
implementors(t(), Absinthe.Type.identifier_t() | Absinthe.Type.Interface.t()) ::
  [Absinthe.Type.Object.t()]

List all implementors of an interface on a schema

Link to this function introspect(schema, opts \\ []) View Source
introspect(schema :: t(), opts :: Absinthe.run_opts()) ::
  Absinthe.run_result()

Run the introspection query on a schema.

Convenience function.

Link to this function introspection_types(schema) View Source
introspection_types(t()) :: [Absinthe.Type.t()]

Get all introspection types

Link to this function lookup_directive(schema, name) View Source
lookup_directive(t(), atom() | binary()) :: Absinthe.Type.Directive.t() | nil

Lookup a directive.

Link to this function lookup_type(schema, type, options \\ [unwrap: true]) View Source
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.

Link to this macro mutation(raw_attrs \\ [name: "RootMutationType"], list) View Source (macro)

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
Link to this macro query(raw_attrs \\ [name: "RootQueryType"], list) View Source (macro)

Defines a root Query object

Link to this function replace_default(middleware_list, new_middleware, map, object) View Source

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
Link to this macro subscription(raw_attrs \\ [name: "RootSubscriptionType"], list) View Source (macro)

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.

Link to this function types(schema) View Source
types(t()) :: [Absinthe.Type.t()]

List all types on a schema

Link to this function used_types(schema) View Source
used_types(t()) :: [Absinthe.Type.t()]

Get all types that are used by an operation

Link to this section Callbacks

Link to this callback context(map) View Source
context(map()) :: map()

List of Plugins to run before / after resolution.

Plugins are modules that implement the Absinthe.Plugin behaviour. These modules have the opportunity to run callbacks before and after the resolution of the entire document, and have access to the resolution accumulator.

Plugins must be specified by the schema, so that Absinthe can make sure they are all given a chance to run prior to resolution.