absinthe v0.2.2 Absinthe.Adapter behaviour

Absinthe supports an adapter mechanism that allows developers to define their schema using one code convention (eg, snake_cased fields and arguments), but accept query documents and return results (including names in errors) in another (eg, camelCase).

Adapters aren’t a part of GraphQL, but a utility that Absinthe adds so that both client and server can use use conventions most natural to them.

Absinthe ships with two adapters:

To set the adapter, you can set an application configuration value:

config :absinthe,
  adapter: Absinthe.Adapter.LanguageConventions

Or, you can provide it as an option to Absinthe.run/3:

Absinthe.run(query, MyApp.Schema,
           adapter: Absinthe.Adapter.LanguageConventions)

Notably, this means you’re able to switch adapters on case-by-case basis. In a Phoenix application, this means you could even support using different adapters for different clients.

A custom adapter module must merely implement the Absinthe.Adapter protocol, in many cases with use Absinthe.Adapter and only overriding the desired functions.

Writing Your Own

Considering the default implementation of the callbacks handle traversing ASTs for you, there’s a good chance all you may need to implement in your adapter is to_internal_name/2 and to_external_name/2.

Check out Absinthe.Adapter.LanguageConventions for a good example.

Summary

Types

The lexical role of a name within the document/schema

Callbacks

Convert the canonical (internal) results to the output (external) representation

Format an error using value for name located at the provided line/column locations

Convert the incoming (external) parsed document to the canonical (internal) representation that matches the schema

Convert a name from an internal name to an external name

Convert a name from an external name to an internal name

Types

role_t :: :operation | :field | :argument | :result

The lexical role of a name within the document/schema.

Callbacks

dump_results(arg0)

Specs

dump_results(Absinthe.Execution.result_t) :: any

Convert the canonical (internal) results to the output (external) representation.

Examples

def dump_results(%{data: data} = results) do
  %{results | data: your_custom_transformation(data)}
end
format_error(arg0, list)

Specs

format_error(Absinthe.Execution.error_info_t, [Absinthe.Execution.error_location_t]) :: Absinthe.Execution.error_t

Format an error using value for name located at the provided line/column locations.

Examples

Here’s what the default implementation does:

iex> format_error(%{name: "foo", role: :field, value: &"missing value '#{&1}'"}, [%{line: 2, column: 4}])
%{message: "missing value 'foo'", locations: [%{line: 2, column: 4}]}

iex> format_error(%{name: "foo", role: :field, value: "missing value"}, [%{line: 2, column: 4}])
%{message: "Field 'foo': missing value", locations: [%{line: 2, column: 4}]}
load_document(arg0)

Convert the incoming (external) parsed document to the canonical (internal) representation that matches the schema.

Examples

def load_document(%{definitions: definitions} = document) do
  %{document | definitions: definitions |> Enum.map(&your_custom_transformation/1)}
end
to_external_name(binary, role_t)

Specs

to_external_name(binary, role_t) :: binary

Convert a name from an internal name to an external name.

Examples

Remove the role-prefix (the inverse of what we did in to_internal_name/2 above):

def to_external_name(internal_name, role) do
  internal_name
  |> String.replace(~r/^#{role}_/, "")
end
to_internal_name(binary, role_t)

Specs

to_internal_name(binary, role_t) :: binary

Convert a name from an external name to an internal name.

Examples

Prefix all names with their role, just for fun!

def to_internal_name(external_name, role) do
  role_name = role |> to_string
  role_name <> "_" <> external_name
end