This is more of a general question, but it really hit home as I read through the “Craft GraphQL APIs” Absinthe book. I noticed this pattern:
defmodule MyContext do
def list_items(args) do
args
|> Enum.reduce(Item, fn
{:order, order}, query ->
query |> order_by({^order, :name})
{:filter, filter}, query ->
query |> filter_with(filter)
end)
|> Repo.all
end
defp filter_with(query, filter) do
Enum.reduce(filter, query, fn
{:name, name}, query ->
from q in query, where: ilike(q.name, ^"%#{name}%")
{:priced_above, price}, query ->
from q in query, where: q.price >= ^price
{:priced_below, price}, query ->
from q in query, where: q.price <= ^price
{:category, category_name}, query ->
from q in query,
join: c in assoc(q, :category),
where: ilike(c.name, ^"%#{category_name}%")
{:tag, tag_name}, query ->
from q in query,
join: t in assoc(q, :tags),
where: ilike(t.name, ^"%#{tag_name}%")
end)
end
end
That seems like a brilliant and expressive way of structuring dynamic search criteria, and it has the benefit of tying in well to the Absinthe resolvers, but my questions are:
- What exactly IS that function expecting? Is it something like this:
args = [{name: "Some name"}, {order: :asc}]
- How could you “feed” into this structure with a standard REST endpoint? I’m having a hard time wrapping my head around how you could send a JSON body (let alone standard form-encoded inputs) that would properly represent linked lists.
- I have noticed that for standard Phoenix controllers, the submitted
params
arrives as a map using binary keys, but here within the GraphQL resolves, the inputs use atom keys. Would it be a good idea to normalize both forms of input?
Thanks – sorry if this is rambling.