absinthe-schema
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAbsinthe - Schema Design
Absinthe - Schema 设计
Comprehensive guide to designing GraphQL schemas with Absinthe in Elixir.
这是一份在Elixir中使用Absinthe设计GraphQL Schema的综合指南。
Key Concepts
核心概念
Type Definitions
类型定义
elixir
defmodule MyApp.Schema.Types do
use Absinthe.Schema.Notation
object :user do
field :id, non_null(:id)
field :name, non_null(:string)
field :email, :string
field :posts, list_of(:post) do
resolve &MyApp.Resolvers.User.posts/3
end
field :inserted_at, :datetime
end
object :post do
field :id, non_null(:id)
field :title, non_null(:string)
field :body, :string
field :author, :user do
resolve &MyApp.Resolvers.Post.author/3
end
end
endelixir
defmodule MyApp.Schema.Types do
use Absinthe.Schema.Notation
object :user do
field :id, non_null(:id)
field :name, non_null(:string)
field :email, :string
field :posts, list_of(:post) do
resolve &MyApp.Resolvers.User.posts/3
end
field :inserted_at, :datetime
end
object :post do
field :id, non_null(:id)
field :title, non_null(:string)
field :body, :string
field :author, :user do
resolve &MyApp.Resolvers.Post.author/3
end
end
endInterfaces
接口
elixir
interface :node do
field :id, non_null(:id)
resolve_type fn
%MyApp.User{}, _ -> :user
%MyApp.Post{}, _ -> :post
_, _ -> nil
end
end
object :user do
interface :node
field :id, non_null(:id)
field :name, non_null(:string)
endelixir
interface :node do
field :id, non_null(:id)
resolve_type fn
%MyApp.User{}, _ -> :user
%MyApp.Post{}, _ -> :post
_, _ -> nil
end
end
object :user do
interface :node
field :id, non_null(:id)
field :name, non_null(:string)
endUnions
联合类型
elixir
union :search_result do
types [:user, :post, :comment]
resolve_type fn
%MyApp.User{}, _ -> :user
%MyApp.Post{}, _ -> :post
%MyApp.Comment{}, _ -> :comment
_, _ -> nil
end
endelixir
union :search_result do
types [:user, :post, :comment]
resolve_type fn
%MyApp.User{}, _ -> :user
%MyApp.Post{}, _ -> :post
%MyApp.Comment{}, _ -> :comment
_, _ -> nil
end
endEnums
枚举
elixir
enum :post_status do
value :draft, as: "draft"
value :published, as: "published"
value :archived, as: "archived"
endelixir
enum :post_status do
value :draft, as: "draft"
value :published, as: "published"
value :archived, as: "archived"
endInput Objects
输入对象
elixir
input_object :create_post_input do
field :title, non_null(:string)
field :body, :string
field :status, :post_status, default_value: :draft
endelixir
input_object :create_post_input do
field :title, non_null(:string)
field :body, :string
field :status, :post_status, default_value: :draft
endBest Practices
最佳实践
- Organize types by domain - Group related types in separate modules
- Use non_null sparingly - Only for truly required fields
- Leverage interfaces - For shared fields across types
- Define input objects - For complex mutation arguments
- Use custom scalars - For dates, UUIDs, JSON, etc.
- 按领域组织类型 - 将相关类型分组到独立模块中
- 谨慎使用non_null - 仅用于真正必填的字段
- 利用接口 - 用于跨类型的共享字段
- 定义输入对象 - 用于复杂的变更参数
- 使用自定义标量类型 - 处理日期、UUID、JSON等类型
Schema Organization
Schema 组织
elixir
defmodule MyApp.Schema do
use Absinthe.Schema
import_types MyApp.Schema.Types
import_types MyApp.Schema.Queries
import_types MyApp.Schema.Mutations
import_types MyApp.Schema.Subscriptions
import_types Absinthe.Type.Custom # DateTime, etc.
query do
import_fields :user_queries
import_fields :post_queries
end
mutation do
import_fields :user_mutations
import_fields :post_mutations
end
subscription do
import_fields :post_subscriptions
end
endelixir
defmodule MyApp.Schema do
use Absinthe.Schema
import_types MyApp.Schema.Types
import_types MyApp.Schema.Queries
import_types MyApp.Schema.Mutations
import_types MyApp.Schema.Subscriptions
import_types Absinthe.Type.Custom # DateTime, etc.
query do
import_fields :user_queries
import_fields :post_queries
end
mutation do
import_fields :user_mutations
import_fields :post_mutations
end
subscription do
import_fields :post_subscriptions
end
endCustom Scalars
自定义标量类型
elixir
scalar :uuid, name: "UUID" do
serialize &to_string/1
parse &parse_uuid/1
end
defp parse_uuid(%Absinthe.Blueprint.Input.String{value: value}) do
case Ecto.UUID.cast(value) do
{:ok, uuid} -> {:ok, uuid}
:error -> :error
end
end
defp parse_uuid(_), do: :errorelixir
scalar :uuid, name: "UUID" do
serialize &to_string/1
parse &parse_uuid/1
end
defp parse_uuid(%Absinthe.Blueprint.Input.String{value: value}) do
case Ecto.UUID.cast(value) do
{:ok, uuid} -> {:ok, uuid}
:error -> :error
end
end
defp parse_uuid(_), do: :errorAnti-Patterns
反模式
- Avoid deeply nested types without pagination
- Don't expose database IDs directly without consideration
- Avoid circular dependencies in type definitions
- Don't skip field descriptions for documentation
- 避免无分页的深度嵌套类型
- 不考虑安全性的情况下,不要直接暴露数据库ID
- 避免类型定义中的循环依赖
- 不要为了省事跳过字段描述(影响文档生成)