absinthe v1.4.13 Absinthe.Resolution.Helpers View Source
Handy functions for returning async or batched resolution functions
Using Absinthe.Schema.Notation
or (by extension) Absinthe.Schema
will
automatically import the batch
and async
helpers. Dataloader helpers
require an explicit import Absinthe.Resolution.Helpers
invocation, since
dataloader is an optional dependency.
Link to this section Summary
Functions
Execute resolution field asynchronously
Batch the resolution of several functions together
Resolve a field with a dataloader source
Resolve a field with Dataloader
Dataloader helper function
Link to this section Types
dataloader_key_fun() :: (Absinthe.Resolution.source(), Absinthe.Resolution.arguments(), Absinthe.Resolution.t() -> {any(), map()})
dataloader_opt() :: {:args, map()} | {:use_parent, true | false}
dataloader_tuple() :: {:middleware, Absinthe.Middleware.Dataloader, term()}
Link to this section Functions
Execute resolution field asynchronously.
This is a helper function for using the Absinthe.Middleware.Async
.
Forbidden in mutation fields. (TODO: actually enforce this)
batch( Absinthe.Middleware.Batch.batch_fun(), term(), Absinthe.Middleware.Batch.post_batch_fun(), opts :: Keyword.t() ) :: {:plugin, Absinthe.Middleware.Batch, term()}
Batch the resolution of several functions together.
Helper function for creating Absinthe.Middleware.Batch
Example
Raw usage:
object :post do
field :name, :string
field :author, :user do
resolve fn post, _, _ ->
batch({__MODULE__, :users_by_id}, post.author_id, fn batch_results ->
{:ok, Map.get(batch_results, post.author_id)}
end)
end
end
end
def users_by_id(_, user_ids) do
users = Repo.all from u in User, where: u.id in ^user_ids
Map.new(users, fn user -> {user.id, user} end)
end
dataloader(Dataloader.source_name()) :: dataloader_tuple()
Resolve a field with a dataloader source.
This function is not imported by default. To make it available in your module do
import Absinthe.Resolution.Helpers
Same as dataloader/3
, but it infers the resource name from the field name.
Examples
field :author, :user, resolve: dataloader(Blog)
This is identical to doing the following.
field :author, :user, resolve: dataloader(Blog, :author, [])
dataloader(Dataloader.source_name(), dataloader_key_fun() | any(), [ dataloader_opt() ]) :: dataloader_tuple()
Resolve a field with Dataloader
This function is not imported by default. To make it available in your module do
import Absinthe.Resolution.Helpers
While on_load/2
makes using dataloader directly easy within a resolver function,
it is often unnecessary to need this level of direct control.
The dataloader/3
function exists to provide a simple API for using dataloader.
It takes the name of a data source, the name of the resource you want to load,
and then a variety of options.
Basic Usage
object :user do
field :posts, list_of(:post),
resolve: dataloader(Blog, :posts, args: %{deleted: false})
field :organization, :organization do
resolve dataloader(Accounts, :organization, use_parent: false)
end
end
Key Functions
Instead of passing in a literal like :posts
or :organization
in as the resource,
it is also possible pass in a function:
object :user do
field :posts, list_of(:post) do
arg :limit, non_null(:integer)
resolve dataloader(Blog, fn user, args, info ->
args = Map.update!(args, :limit, fn val ->
max(min(val, 20), 0)
end)
{:posts, args}
end)
end
end
In this case we want to make sure that the limit value cannot be larger than
20
. By passing a callback function to dataloader/2
we can ensure that
the value will fall nicely between 0 and 20.
Options
:args
default:%{}
. Any arguments you want to always pass into theDataloader.load/4
call. Resolver arguments are merged into this value and, in the event of a conflict, the resolver arguments win.:use_parent
default:true
. This option affects whether or not thedataloader/2
helper will use any pre-existing value on the parent. IE if you return%{author: %User{...}}
from a blog post the helper will by default simply use the pre-existing author. Set it to false if you always want it to load it fresh.
Ultimately, this helper calls Dataloader.load/4
using the loader in your context, the source you provide, the tuple {resource, args}
as the batch key, and then the parent value of the field
def dataloader(source_name, resource) do
fn parent, args, %{context: %{loader: loader}} ->
args = Map.merge(opts[:args] || %{}, args)
loader
|> Dataloader.load(source_name, {resource, args}, parent)
|> on_load(fn loader ->
{:ok, Dataloader.get(loader, source_name, {resource, args}, parent)}
end)
end
Dataloader helper function
This function is not imported by default. To make it available in your module do
import Absinthe.Resolution.Helpers
This function helps you use data loader in a direct way within your schema.
While normally the dataloader/1,2,3
helpers are enough, on_load/2
is useful
when you want to load multiple things in a single resolver, or when you need
fine grained control over the dataloader cache.
Examples
field :reports, list_of(:report) do
resolve fn shipment, _, %{context: %{loader: loader}} ->
loader
|> Dataloader.load(SourceName, :automatic_reports, shipment)
|> Dataloader.load(SourceName, :manaul_reports, shipment)
|> on_load(fn loader ->
reports =
loader
|> Dataloader.get(SourceName, :automatic_reports, shipment)
|> Enum.concat(Dataloader.load(loader, SourceName, :manaul_reports, shipment))
|> Enum.sort_by(&reported_at/1)
{:ok, reports}
end)
end
end