A dataloader pattern giving the benefits of graphql for liveview

Yesterday I spent an hour architecting a not-in-graphql way to do one of the things I love most about graphql in absinthe, batch loading… the main thing about batch loading that I love so much is that you delegate each node in the graph down to its own dataloader/context/service module. Meaning that if you resolve A based on some acl and derive down to B by traversing the graph, you don’t have to think about acl for B until you hit the service layer for it. This requires you to propagate the authority / current_user down there ofc, but thats a small tradeoff.

This lets you do what is essentially a Repo.preload chain with added benefit of batch loading to avoid n+1 when the tree expands + acl checks at every level.

I have been playing with a pattern of Repo.with_acl_preload(entity, [..paths]) that essentially “resolves” in graphql style, but based on schema metadata.

How to use it is very simple:

  1. add a def data_provider(), do: FooContext # or whatever place you load data for this, can also be a tuple if you want to have a data provider for many schemas in one provider, like the phoenix generators tend to make newbies do
  2. Implement the data_provider in the loader module. It gives you three args, auth (or current_user, whatever you need to derive acls), ids (just like batch load ids in absinthe) and params which is basically a list of whatever other params you want to pass down if your path instead of doing [post: :comments] you can do [post: {:comments, [param1, param2]] typically used if you want to prop drill some info down to the loader. One such example can be that you want to mask some fields for example based on the acl of the calling authority. Another can be a list of columns you want pulled out through struct query api etc…

What I have outstanding to solve is how play with this and see that it works just as well for belongs to as it does for has many. But I have faith that this could be a lib to SERIOUSLY improve DX and speed of implementation in liveview similar to how graphql can speed up prototyping in frontend, without sacrificing good patterns for ACL and not just having a ton of preloads or a mess of functions in all directions.

Any feedback / ideas / comments before I dig deeper to release a 0.1 version?

4 Likes

It should be noted, I have also experimented with using Absinthe data modeling for liveview and just calling it through Absinthe.run(…schema stuff…) but it just flat out sucks for mutation usage since liveview form lifecycle is so tied to schemas. It just felt like re-implementing the layer twice all the time. It works fantastically for querying though, with simple “something updated, just rerun query and let the data propagate down the render tree” logic making it a breeze to work with.

That niceness is what has prompted me to dig into this.

I have nothing constructive to add but that I am interested in seeing/experimenting with this when you release it. :slight_smile:

1 Like