absinthe v1.4.0 Absinthe.Resolution.Helpers View Source
Handy functions for returning async or batched resolution functions
It is automatically imported into all modules using Absinthe.Schema.Notation
or (by extension) Absinthe.Schema
.
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
async((() -> term), Keyword.t) :: {:middleware, Absinthe.Middleware.Async, term}
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.
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
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 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