thojanssens1

thojanssens1

Absinthe GraphQL: best practice for object graph

I work on a calendar app, and to query appointments, my query document looks like below:

me {
  calendar {
    appointments { ... }
    availabilities { ... }
  }
}

To access the appointments, I have to resolve the current user, then the calendar, then finally the appointments.

This follows the Ecto schemas associations.

And in the JS client, I have account.calendar.appointments.

It made me thinking, shouldn’t I also offer the possibility to fetch the appointments directly from the account?

me {
  appointments { ... }
  availabilities { ... }
}

But then I need to handle two function clauses for the resolver:

appointments(%Calendar{} = calendar, %{date_range: date_range}, _)
appointments(%Account{} = account, %{date_range: date_range}, _)

Question:
If I further reason like that for other parts, I’ll have resolvers that resolve an entity for multiple parent types with multiple clauses, is that a normal thing to have? Do you resolve entities for multiple parents to avoid having to access deeply nested objects?
Or do you design the graph closely following the Ecto schema associations (such as seen in first query document)? I’m confused..

Secondly, when the JS client requests for example the appointments, I end up with an account (as I’m using the viewer ‘me’ pattern as seen above), and not directly appointments (or as in my first example, an account, then a calendar, then appointments). So code/variables/function names tends to be a little incoherent on the JS client side, e.g. a fetchAppointments function requesting the appointments will result into an account.. there are many ways to solve this issue, but how do you deal with that?

Any guidance is welcome!

First Post!

cjbottaro

cjbottaro

I’m interested in how people approach this also. We got a fairly large Absinthe project and faced some of these same decisions.

For the most part no. It doesn’t feel very dry to me (too many ways to skin a cat). Or it feels like it’s just making convenience functions just for the reason of making your query smaller.

The biggest issue for us (and this might relate to some of your issues) is that we use Dataloader exclusively, which makes it where resolvers can’t “call” other resolvers, so when we want to share functionality, we make “resolver helpers” that different resolvers can call. Which looks like this for your example:

def appointments(%Calendar{} = calendar, args, res) do
  res.context.loader
  |> load_appointments(calendar, args, fn _loader, appointments ->
    {:ok, appointments}
  end)
end

def appointments(%Account{} = account, args, res) do
  res.context.loader
  |> load(:db, :calendar, account)
  |> on_load(fn loader, calendar ->
    loader
    |> load_appointments(calendar, args, fn _loader, appointments ->
      {:ok, appointments}
    end)
  end)
end

(we have our own Dataloader helpers that make this API possible; I know it’s not standard)

I don’t know if that’s the best way to share functionality between Dataloader based resolvers, but it works.

Where Next?

Popular in Discussions Top

Jayshua
I recently came across the javascript library htmx. It reminded me a lot of liveview so I thought the community here might be interested....
New
sashaafm
I’m trying to evaluate the best combo/stack for a BEAM Web app. Right now I’m exploring Yaws a bit, after having dealt with Phoenix for a...
New
jeramyRR
This is an interesting article to read. Elixir’s performance, like usual, is excellent. However, it seems like the high CPU usage is co...
New
AstonJ
If a newbie asked you about Phoenix Contexts, how would you explain the basics to them? Feel free to be as concise or in-depth as you li...
New
rower687
Hi all, I’ve been reading a lot about the “let it crash” term and how supervising processes and the whole messaging passing make an elixi...
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
joeerl
I’m playing with Elixir - It’s fun. I think @rvirding does give Elixir courses these days. Re: files and database - when I given Erlang ...
New
Owens
Hello all, I am developing a new mobile app with Flutter frontend and Phoenix backend. The mobile app has real-time task management and c...
New
griffinbyatt
Sobelow Sobelow is a security-focused static analysis tool for the Phoenix framework. For security researchers, it is a useful tool for g...
New
opsb
We’re considering our architecture from a viewpoint of scaling our traffic heavily over the next 6 months. Our current deployment is runn...
New

Other popular topics Top

johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31142 143
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement