Created
April 24, 2025 17:55
-
-
Save shiryel/ba940fe103c2d05cd443fe2db30e0148 to your computer and use it in GitHub Desktop.
elixir absinthe relay with dataloader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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