Reusing Absinthe resolvers on denormalized fields

Not sure how to ask this other than show an example…

Consider a GraphQL query like this:

{
  posts {
    comment_authors
    
    comments {
      author
    }
  }
}

And we have a comment.author resolver that uses a helper function:

field :author, :string, resolve: fn comment, _args, res ->
  res.context.loader
  |> load_author(comment, fn _loader, author ->
    {:ok, author}
  end)
end

def load_author(loader, comment, f) do
  loader
  |> load(Repo, :author, comment)
  |> on_load(fn loader ->
    author = get(Repo, :author, comment)
    f.(loader, author)
  end)
end

How can I reuse that helper function for posts.comment_authors?

field :comment_authors, resolve: fn post, _args, res ->
  res.context.loader
  |> load(Repo, :comments, post)
  |> on_load(fn loader ->
    comments = get(Repo, :comments, post)
    authors = Enum.map(comments, fn comment ->
      load_author(loader, comment, fn loader, author ->
        author
      end)
    end)
    {:ok, authors}
  end)
end

Obviously doesn’t work; authors will be a list of functions.

What is the best way to accomplish this goal? Thanks for the help.

field :comment_authors, list_of(:string), resolve: fn post, _args, res ->
  res.context.loader
  |> load(Repo, :comments, post)
  |> on_load(fn loader ->
    comments = get(Repo, :comments, post)

    loader
    |> Dataloader.load_many(Repo, :authors, comments)
    |> on_load(fn loader ->
      {:ok, Dataloader.get_many(loader, Repo, :authors, comments)}
    end)
  end)
end

Basically, you nest some on_load clauses.

1 Like

The issue is that in my real use case, my helper function is extremely complicated. And it only works on a single record.

I guess the answer is to make my helper function work on multiple records, but that just adds to the complication. There is lots of conditional dataloading, so I’ll have to separate my records into several groups, each of which have different rules of for what needs to be loaded for that group.