absinthe_relay_oxo v1.2.1 Absinthe.Relay.Node

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:

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

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 easly create object types that can be looked up using this field:

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; a Base64 string that’s built using the object type name and the raw, internal identifier. All of this is handled for you automatically by prefixing your object type definition with "node ".

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

Macros

For more details on node-related macros, see Absinthe.Relay.Node.Notation.

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

Functions

default_id_fetcher(arg1, arg2)

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 returns nil
  • If it’s not nil, it coerces it to a binary using Kernel.to_string/1

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
from_global_id(global_id, schema)

Specs

from_global_id(binary, atom) ::
  {:ok, %{type: atom, id: binary}} |
  {:error, binary}

Parse a global ID, given a schema.

Examples

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"}
to_global_id(node_type, source_id)

Specs

to_global_id(atom | binary, integer | binary | nil) ::
  binary |
  nil

Generate a global ID given a node type name and an internal (non-global) ID

Examples

iex> to_global_id("Person", "123")
"UGVyc29uOjEyMw=="
iex> to_global_id(:person, "123", SchemaWithPersonType)
"UGVyc29uOjEyMw=="
iex> to_global_id(:person, nil, SchemaWithPersonType)
"No source non-global ID value given"
to_global_id(node_type, source_id, schema)