Packages
absinthe
1.2.0
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
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/absinthe/schema.ex
defmodule Absinthe.Schema do
import Absinthe.Schema.Notation
@moduledoc """
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.Scheme.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.
"""
@typedoc """
A module defining a schema.
"""
@type t :: atom
alias Absinthe.Type
alias Absinthe.Language
alias __MODULE__
defmacro __using__(opts \\ []) do
quote do
use Absinthe.Schema.Notation, unquote(opts)
import unquote(__MODULE__), only: :macros
import_types Absinthe.Type.BuiltIns
@after_compile unquote(__MODULE__)
@behaviour unquote(__MODULE__)
end
end
@callback resolution_plugins() :: [Absinthe.Resolution.Plugin.t]
@doc false
def __after_compile__(env, _bytecode) do
[
env.module.__absinthe_errors__,
Schema.Rule.check(env.module)
]
|> List.flatten
|> case do
[] ->
nil
details ->
raise Absinthe.Schema.Error, details
end
end
@default_query_name "RootQueryType"
@doc """
Defines a root Query object
"""
defmacro query(raw_attrs, [do: block]) do
attrs = raw_attrs
|> Keyword.put_new(:name, @default_query_name)
Absinthe.Schema.Notation.scope(__CALLER__, :object, :query, attrs, block)
end
@doc """
Defines a root Query object
"""
defmacro query([do: block]) do
Absinthe.Schema.Notation.scope(__CALLER__, :object, :query, [name: @default_query_name], block)
end
@default_mutation_name "RootMutationType"
@doc """
Defines a root Mutation object
"""
defmacro mutation(raw_attrs, [do: block]) do
attrs = raw_attrs
|> Keyword.put_new(:name, @default_mutation_name)
Absinthe.Schema.Notation.scope(__CALLER__, :object, :mutation, attrs, block)
end
@doc """
Defines a root Mutation object
"""
defmacro mutation([do: block]) do
Absinthe.Schema.Notation.scope(__CALLER__, :object, :mutation, [name: @default_mutation_name], block)
end
@default_subscription_name "RootSubscriptionType"
@doc """
Defines a root Subscription object
"""
defmacro subscription(raw_attrs, [do: block]) do
attrs = raw_attrs
|> Keyword.put_new(:name, @default_subscription_name)
Absinthe.Schema.Notation.scope(__CALLER__, :object, :subscription, attrs, block)
end
@doc """
Defines a root Subscription object
"""
defmacro subscription([do: block]) do
Absinthe.Schema.Notation.scope(__CALLER__, :object, :subscription, [name: @default_subscription_name], block)
end
@doc """
Defines a custom default resolve function for the schema.
"""
defmacro default_resolve(func) do
Module.put_attribute(__CALLER__.module, :absinthe_custom_default_resolve, func)
:ok
end
# Lookup a directive that in used by/available to a schema
@doc """
Lookup a directive.
"""
@spec lookup_directive(t, atom | binary) :: Type.Directive.t | nil
def lookup_directive(schema, name) do
schema.__absinthe_directive__(name)
end
@doc """
Lookup a type by name, identifier, or by unwrapping.
"""
@spec lookup_type(atom, Type.wrapping_t | Type.t | Type.identifier_t, Keyword.t) :: Type.t | nil
def lookup_type(schema, type, options \\ [unwrap: true]) do
cond do
Type.wrapped?(type) ->
if Keyword.get(options, :unwrap) do
lookup_type(schema, type |> Type.unwrap)
else
type
end
is_atom(type) ->
schema.__absinthe_type__(type)
is_binary(type) ->
schema.__absinthe_type__(type)
true ->
type
end
end
@doc """
List all types on a schema
"""
@spec types(t) :: [Type.t]
def types(schema) do
schema.__absinthe_types__
|> Map.keys
|> Enum.map(&lookup_type(schema, &1))
end
@doc """
Get all concrete types for union, interface, or object
"""
@spec concrete_types(t, Type.t) :: [Type.t]
def concrete_types(schema, %Type.Union{} = type) do
Enum.map(type.types, &lookup_type(schema, &1))
end
def concrete_types(schema, %Type.Interface{} = type) do
implementors(schema, type)
end
def concrete_types(_, %Type.Object{} = type) do
[type]
end
@doc """
List all directives on a schema
"""
@spec directives(t) :: [Type.Directive.t]
def directives(schema) do
schema.__absinthe_directives__
|> Map.keys
|> Enum.map(&lookup_directive(schema, &1))
end
@doc """
List all implementors of an interface on a schema
"""
@spec implementors(t, atom) :: [Type.Object.t]
def implementors(schema, ident) when is_atom(ident) do
schema.__absinthe_interface_implementors__
|> Map.get(ident, [])
|> Enum.map(&lookup_type(schema, &1))
end
def implementors(schema, %Type.Interface{} = iface) do
implementors(schema, iface.__reference__.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(schema)
|> Enum.find(fn
%{name: name} ->
name == ast_type.name
end)
end
end