I seem to have managed to solve this by using the Dataloader.KV source.
First, I defined a dataloader function to batch load my users:
defmodule MyApp.VoteResultDataLoader do
def load({:top_upvoted_user, _}, vote_results) do
users =
vote_results
|> Enum.map(& &1.user_id)
|> get_user_list()
vote_results
|> Map.new(fn vr -> {vr, Map.get(users, vr.user_id)} end)
end
end
One thing that wasn’t obvious or well documented is that load/2 must return a Map whose keys are the original parent entities passed to the function. E.g.
%{
%{id: 1, score: 5, user_id: 14} => %MyApp.User{id: 123, ...etc}
}
Once I have my dataloader function I can add it as a source to my schema’s dataloader:
Dataloader.add_source(
:vote_results,
Dataloader.KV.new(&MyApp.VoteResultDataLoader.load/2)
)
Finally, in my object resolution I just use the dataloader/1 function:
object :vote_result do
field(:top_upvoted_user, non_null(:user), resolve: dataloader(:vote_results))
end
I’m not sure if I’ve arranged everything as well as it can be. I’m never that sure where to put this dataloader logic and to what part of my app it belongs in. But this will depend on one’s own application.






















