View Source Absinthe.Relay.Node (absinthe_relay v1.5.2)
Support for global object identification.
The node
macro can be used by schema designers to add required
"object identification" support for object types, and to provide a unified
interface for querying them.
More information can be found at:
- https://relay.dev/docs/guides/graphql-server-specification/#object-identification
- https://facebook.github.io/relay/graphql/objectidentification.htm
interface
Interface
Define a node interface for your schema, providing a type resolver that, given a resolved object can determine which node object type it belongs to.
node interface do
resolve_type fn
%{age: _}, _ ->
:person
%{employee_count: _}, _ ->
:business
_, _ ->
nil
end
end
This will create an interface, :node
that expects one field, :id
, be
defined -- and that the ID will be a global identifier.
If you use the node
macro to create your object
types (see "Object" below),
this can be easily done, layered on top of the standard object type definition
style.
field
Field
The node field provides a unified interface to query for an object in the
system using a global ID. The node field should be defined within your schema
query
and should provide a resolver that, given a map containing the object
type identifier and internal, non-global ID (the incoming global ID will be
parsed into these values for you automatically) can resolve the correct value.
query do
# ...
node field do
resolve fn
%{type: :person, id: id}, _ ->
{:ok, Map.get(@people, id)}
%{type: :business, id: id}, _ ->
{:ok, Map.get(@businesses, id)}
end
end
end
This creates a field, :node
, with one argument: :id
. This is expected to
be a global ID and, once resolved, will result in a value whose type
implements the :node
interface.
Here's how you easily create object types that can be looked up using this field:
object
Object
To play nicely with the :node
interface and field, explained above, any
object types need to implement the :node
interface and generate a global
ID as the value of its :id
field. Using the node
macro, you can easily do
this while retaining the usual object type definition style.
node object :person do
field :name, :string
field :age, :string
end
This will create an object type, :person
, as you might expect. An :id
field is created for you automatically, and this field generates a global ID;
an opaque string that's built using a global ID translator (by default a
Base64 implementation). All of this is handled for you automatically by
prefixing your object type definition with "node "
.
By default, type of :id
field is ID
. But you can pass custom type in :id_type
attribute:
node interface id_type: :uuid do
resolve_type fn
...
end
end
node field id_type: :uuid do
resolve fn
...
end
end
node object :thing, id_type: :uuid do
field :name, :string
end
Or you can set it up globally via application config:
config Absinthe.Relay,
node_id_type: :uuid
The raw, internal value is retrieved using default_id_fetcher/2
which just
pattern matches an :id
field from the resolved object. If you need to
extract/build an internal ID via another method, just provide a function as
an :id_fetcher
option.
For instance, assuming your raw internal IDs were stored as :_id
, you could
configure your object like this:
node object :thing, id_fetcher: &my_custom_id_fetcher/2 do
field :name, :string
end
For instructions on how to change the underlying method of decoding/encoding
a global ID, see Absinthe.Relay.Node.IDTranslator
.
macros
Macros
For more details on node-related macros, see
Absinthe.Relay.Node.Notation
.
Link to this section Summary
Functions
The default ID fetcher used to retrieve raw, non-global IDs from values.
Parse a global ID, given a schema.
Generate a global ID given a node type name and an internal (non-global) ID given a schema.
Link to this section Types
Specs
global_id() :: binary()
Link to this section Functions
Specs
default_id_fetcher(any(), Absinthe.Resolution.t()) :: nil | binary()
The default ID fetcher used to retrieve raw, non-global IDs from values.
- Matches
:id
out of the value.- If it's
nil
, it returnsnil
- If it's not nil, it coerces it to a binary using
Kernel.to_string/1
- If it's
examples
Examples
iex> default_id_fetcher(%{id: "foo"})
"foo"
iex> default_id_fetcher(%{id: 123})
"123"
iex> default_id_fetcher(%{id: nil})
nil
iex> default_id_fetcher(%{nope: "no_id"})
nil
Specs
from_global_id(nil, Absinthe.Schema.t()) :: {:ok, nil}
from_global_id(global_id(), Absinthe.Schema.t()) :: {:ok, %{type: atom(), id: binary()}} | {:error, binary()}
Parse a global ID, given a schema.
To change the underlying method of decoding a global ID,
see Absinthe.Relay.Node.IDTranslator
.
examples
Examples
For nil
, pass-through:
iex> from_global_id(nil, Schema)
{:ok, nil}
For a valid, existing type in Schema
:
iex> from_global_id("UGVyc29uOjE=", Schema)
{:ok, %{type: :person, id: "1"}}
For an invalid global ID value:
iex> from_global_id("GHNF", Schema)
{:error, "Could not decode ID value `GHNF'"}
For a type that isn't in the schema:
iex> from_global_id("Tm9wZToxMjM=", Schema)
{:error, "Unknown type `Nope'"}
For a type that is in the schema but isn't a node:
iex> from_global_id("Tm9wZToxMjM=", Schema)
{:error, "Type `Item' is not a valid node type"}
Specs
to_global_id( atom() | binary(), integer() | binary() | nil, Absinthe.Schema.t() | nil ) :: global_id() | nil
Generate a global ID given a node type name and an internal (non-global) ID given a schema.
To change the underlying method of encoding a global ID,
see Absinthe.Relay.Node.IDTranslator
.
examples
Examples
iex> to_global_id("Person", "123")
"UGVyc29uOjEyMw=="
iex> to_global_id(:person, "123", SchemaWithPersonType)
"UGVyc29uOjEyMw=="
iex> to_global_id(:person, nil, SchemaWithPersonType)
nil