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:
Absinthe.Adapter.Passthrough
, which is a no-op adapter and makes no modifications. (This is the default.)Absinthe.Adapter.LanguageConventions
, which expects schemas to be defined insnake_case
(the standard Elixir convention), translating to/fromcamelCase
for incoming query documents and outgoing results.
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
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
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
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}]}
Specs
load_document(Absinthe.Language.Document.t) :: Absinthe.Language.Document.t
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
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
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