Hey, all! I’m stumped with what seems like a fairly simple issue. I’m using Phoenix and Absinthe to set up a GraphQL backend. Ideally, I’d like to have a structure that looks like this:
{
posts {
published {
title
}
draft {
title
}
}
}
To do so, I thought I would need to delegate the posts
field in my schema to an object that responded to published
and draft
. I did this like so:
# In my Schema
field :posts, type: :posts_by_state
# In my Post type definitions
object :post do
# ...
end
object :posts_by_state do
field :published, list_of(:post) do
resolve fn _, _, _ -> ... end
end
field :draft, list_of(:post) do
resolve fn _, _, _ -> ... end
end
end
This didn’t work and instead returns null
for the entire posts
field. However, if I changed the posts
field in the schema to include a “blank” resolver, it worked as expected:
field :posts, type: :posts_by_state do
resolve fn _, _, _ -> {:ok, []} end
end
Is this best practice or is there a better way to tell a field to delegate entirely to an object? More generally, is there a better way to structure this?
Hey @kieraneglin, good question. The behavior you’re seeing here is expected, although usually the dummy resolver you’d use is:
field :posts, type: :posts_by_state do
resolve fn _, _, _ -> {:ok, %{}} end
end
If you don’t specify a resolver, Absinthe will use the default resolver, which does a Map.get(parent_value, field_name)
. In your case if the root value is %{}
(which it is by default) then if you don’t specify a posts
field resolver Absinthe does Map.get(%{}, :posts)
which of course returns nil
. Since the field returns nil, no sub fields are executed.
Really though, these seem more like things that should be arguments IE:
{
published: posts(status: PUBLISHED) { title }
draft: posts(status: DRAFT) { title }
}
4 Likes
Thank you for the thorough answer! I agree with your point about these becoming arguments. However, I ran into a problem: I wanted to use some authorization middleware that would only allow authors
to see all draft posts (this example is getting contrived, but I promise we’re trying to solve a similar issue in practice). I imagined something like this:
object :posts_by_state do
field :published, list_of(:post) do
# Everyone can access this!
resolve fn _, _, _ -> ... end
end
field :draft, list_of(:post) do
# Only `authors` can access this
middleware(Middleware.Authorization, :author)
resolve fn _, _, _ -> ... end
end
end
It is still viable to accomplish this with arguments or should I be approaching this problem in a different way?
Sorry to bump, but I’m still unsure about my follow up question re: serving up similar endpoints based on permission level. Do you have any advice on the matter?
Thank you!