From absinthe-graphql
Guides GraphQL resolver implementation in Absinthe with patterns, Dataloader integration for N+1 prevention, batching, middleware, context, and error handling.
npx claudepluginhub thebushidocollective/han --plugin absinthe-graphqlThis skill uses the workspace's default tool permissions.
Guide to implementing efficient and maintainable resolvers in Absinthe.
Implements GraphQL resolvers covering function signatures, field resolvers, context management, DataLoader batching, error handling, authentication, and testing strategies.
Guides designing GraphQL schemas with Absinthe in Elixir. Covers type definitions, interfaces, unions, enums, input objects, schema organization, custom scalars, and best practices.
Implements GraphQL resolvers with clean separation of data fetching, business logic, and response shaping. Useful for writing queries/mutations, debugging N+1 issues, and setting up context.
Share bugs, ideas, or general feedback.
Guide to implementing efficient and maintainable resolvers in Absinthe.
defmodule MyApp.Resolvers.User do
alias MyApp.Accounts
def list_users(_parent, args, _resolution) do
{:ok, Accounts.list_users(args)}
end
def get_user(_parent, %{id: id}, _resolution) do
case Accounts.get_user(id) do
nil -> {:error, "User not found"}
user -> {:ok, user}
end
end
def create_user(_parent, %{input: input}, _resolution) do
case Accounts.create_user(input) do
{:ok, user} -> {:ok, user}
{:error, changeset} -> {:error, changeset}
end
end
end
def current_user(_parent, _args, %{context: %{current_user: user}}) do
{:ok, user}
end
def current_user(_parent, _args, _resolution) do
{:error, "Not authenticated"}
end
defmodule MyApp.Schema do
use Absinthe.Schema
def context(ctx) do
loader =
Dataloader.new()
|> Dataloader.add_source(MyApp.Repo, Dataloader.Ecto.new(MyApp.Repo))
Map.put(ctx, :loader, loader)
end
def plugins do
[Absinthe.Middleware.Dataloader] ++ Absinthe.Plugin.defaults()
end
end
# In type definitions
object :user do
field :posts, list_of(:post) do
resolve dataloader(MyApp.Repo)
end
end
defmodule MyApp.Loaders.User do
def data() do
Dataloader.KV.new(&fetch/2)
end
defp fetch(:posts_count, user_ids) do
counts = MyApp.Posts.count_by_user_ids(user_ids)
Map.new(user_ids, fn id ->
{id, Map.get(counts, id, 0)}
end)
end
end
defmodule MyApp.Middleware.Auth do
@behaviour Absinthe.Middleware
def call(resolution, _config) do
case resolution.context do
%{current_user: %{}} ->
resolution
_ ->
resolution
|> Absinthe.Resolution.put_result({:error, "Unauthorized"})
end
end
end
# Apply to fields
field :admin_data, :string do
middleware MyApp.Middleware.Auth
resolve &MyApp.Resolvers.Admin.get_data/3
end
defmodule MyApp.Resolvers.Helpers do
def handle_changeset_errors(%Ecto.Changeset{} = changeset) do
errors =
Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", to_string(value))
end)
end)
{:error, errors}
end
end