Paginating associations with Absinthe Relay connections

Most articles on Absinthe suggest the use of dataloader when dealing with associations.
The reason being it batches successive associations and handles them all within a single query.

This does indeed work fine for most cases, but certainly not when doing pagination.
That is because one would ideally want to limit and offset per-association, which in turn
means applying pagination on a per-join basis and is not possible really with regular joins.
A feasible solution could be doing lateral joins with paginated subqueries, but because of
obvious performance reasons and because it is not completely supported within ecto and
its adapters, I guess we can agree not to go that way and look for a different approach.

Therefore, I am wondering. Specially to those doing GraphQL in production, how do you
approach this issue and paginate your associations exactly? Much appreciated! :slight_smile:

1 Like

Here is one association I am using in a GraphQL schema…

  node object :player do
    field :internal_id, :integer, do: resolve &resolve_internal_id/2
    field :last_name, :string
    field :first_name, :string

    connection field :games, node_type: :game do
      arg :order, type: :sort_order, default_value: :asc
      arg :filter, :game_filter
      resolve &ChessResolver.list_player_games/3
    end

    # Timestamps
    field :inserted_at, :naive_datetime
    field :updated_at, :naive_datetime
  end

and the corresponding resolver.

alias Absinthe.Relay.Connection
...
  def list_player_games(_, args, %{source: player}) do
    Chess.list_player_games_query(player, args)
    |> Connection.from_query(&Chess.process_repo/1, args)
  end

I can query game association with usual relay filter (first, last etc.)

I thought pagination was one of the main reason for using Relay :slight_smile:

That does work indeed but say you were doing a second connection within the games connection.
If you were to do all connections this way, you’d end up querying the database multiple times, once
per successive connection, and running into the so called N+1 problem. That’s why dataloader comes
in handy, because it batches the associations and turns them into a single query automagically. :slight_smile:

That’s why I was wondering if there was a solution really besides just doing separate queries…
If otherwise, I’d also be looking for a generic resolver that would work for all associations, so that
I wouldn’t have to care really about coding each of the resolvers manually. :thinking:

Most of the new code use dataloader. It was not useful in the simple case I had… but I would use it now (code is quite old)