Absinthe: Passing unresolved fields through resolvers
In most cases child field resolvers retrieve data independently. There is a case when this may not be the pattern: an Ecto association that is preloaded. In this case, the child resolver should receive the preloaded data at the first positional argument of a 3 arity resolver function. This can be matched to short cut doing an independent query. In the 1.4 version of Absinthe, this passing of data from parent to child resolver drops fields if the top level is a struct
and the child is a struct
. The solution seems to be to use Map.from_struct
to convert the top level or the map subfield to a regular map. The parent
arg will then have the deeper keys.
Concretely, the schema...
object :item do
field :id, :id
field :qty, :integer
field :sku, :id
field :cart, :cart do
resolve &cart_resolve/3
end
end
With a top level resolver with the Ecto query:
def item_resolve( _, %{ id: id }, _ ) do
from( item in Item,
where: item.id == ^id,
join: cart in assoc( item, :cart ),
preload: [ cart: cart ]
)
|> Repo.one
|> result_tuple
end
From this both the item and the cart will be loaded in one query, no sense in the cart_resolve
doing another query. Such as:
def cart_resolve( %{ cart: cart }, _, _ ) do
{ :ok, <do something with the cart data> }
end
The problem is that cart_resolve
won't receive the cart from item_resolve
. The fix is to change the end of item_resolve
to:
...
|> Repo.one
|> Map.from_struct
|> result_tuple
In this case, if cart_resolve
doesn't really do anything in the pass through, then the field resolver for :cart can be omitted. If you do that, you still need to convert at least the top resolver result to a regular map otherwise the subfield ( :cart in this case) will be nil.