Skip to content

Instantly share code, notes, and snippets.

@shiryel
Created April 24, 2025 17:55
Show Gist options
  • Save shiryel/ba940fe103c2d05cd443fe2db30e0148 to your computer and use it in GitHub Desktop.
Save shiryel/ba940fe103c2d05cd443fe2db30e0148 to your computer and use it in GitHub Desktop.
elixir absinthe relay with dataloader
defmodule AbsintheHelpers do
import Absinthe.Resolution.Helpers
alias Absinthe.Relay.Connection
def relay_dataloader(source, opts \\ []) do
fn parent,
raw_args,
%{context: %{loader: loader}, source: %{business_id: _business_id}, definition: definition} =
ctx ->
resource = definition.schema_node.identifier
args =
decode_cursors(raw_args)
|> Map.update(:limit, opts[:max] || 50, fn v -> v + 1 end)
batch_key = {resource, args}
Dataloader.load(loader, source, batch_key, parent)
|> on_load(fn loader ->
Dataloader.get(loader, source, batch_key, parent)
|> from_list(args, opts)
end)
end
end
def from_list(records, args, opts \\ []) do
{:ok, _direction, limit} = Connection.limit(args, opts[:max])
count = Enum.count(records)
opts =
opts
|> Keyword.put(:has_previous_page, Map.has_key?(args, :last) && count > args.last)
|> Keyword.put(:has_next_page, Map.has_key?(args, :first) && count > args.first)
from_slice(Enum.take(records, limit - 1), opts)
end
def from_slice(items, opts) do
{edges, first, last} = build_cursors(items)
page_info = %{
start_cursor: first,
end_cursor: last,
has_previous_page: Keyword.get(opts, :has_previous_page, false),
has_next_page: Keyword.get(opts, :has_next_page, false)
}
{:ok, %{edges: edges, page_info: page_info}}
end
defp build_cursors([]), do: {[], nil, nil}
defp build_cursors([item | items]) do
first = encode_cursor(item)
edge = %{node: item, cursor: first}
{edges, last} = do_build_cursors(items, [edge], first)
{edges, first, last}
end
defp do_build_cursors([], edges, last), do: {Enum.reverse(edges), last}
defp do_build_cursors([item | rest], edges, _last) do
cursor = encode_cursor(item)
edge = %{node: item, cursor: cursor}
do_build_cursors(rest, [edge | edges], cursor)
end
defp encode_cursor(item) do
"inserted_at:#{item.inserted_at}" |> Base.encode64()
end
defp decode_cursors(args) do
args
|> Enum.map(fn
{:after, v} -> {:after, decode_cursor!(v)}
{:before, v} -> {:before, decode_cursor!(v)}
{k, v} -> {k, v}
end)
|> Map.new()
end
defp decode_cursor!(cursor) do
"inserted_at:" <> date = Base.decode64!(cursor)
date
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment