Absinthe解析器Skill absinthe-resolvers

Absinthe解析器技能专注于使用Elixir框架Absinthe高效实现GraphQL解析器,涵盖解析器模式、Dataloader集成、批处理查询和错误处理,旨在提升后端API的性能和可维护性。关键词:GraphQL, Absinthe, 解析器, Elixir, 后端开发, Dataloader, 错误处理, 中间件。

后端开发 0 次安装 0 次浏览 更新于 3/25/2026

名称: absinthe-resolvers 用户可调用: false 描述: 在使用Absinthe实现GraphQL解析器时使用。涵盖解析器模式、dataloader集成、批处理和错误处理。

Absinthe - 解析器

在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 -> {: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, "未认证"}
end

Dataloader集成

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

# 在类型定义中
object :user do
  field :posts, list_of(:post) do
    resolve dataloader(MyApp.Repo)
  end
end

自定义Dataloader源

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

最佳实践

  1. 使用Dataloader - 防止N+1查询
  2. 保持解析器简洁 - 委托给上下文模块
  3. 优雅处理错误 - 返回有意义的错误消息
  4. 使用中间件 - 用于跨领域关注点如认证
  5. 批量相关查询 - 使用dataloader批处理

中间件

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, "未授权"})
    end
  end
end

# 应用于字段
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

反模式

  • 避免在解析器中包含业务逻辑
  • 不要在解析器中直接查询数据库
  • 避免返回原始的Ecto changesets
  • 不要跳过错误处理