elixirgraphqlphoenix-frameworkabsinthe

Creating a GraphQL field without a resolver


I'm using Elixir with 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?


Solution

  • This answer was provided by benwilson512 on the Elixir forums. Posting here to share the answer


    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 }
    }