Files
2026-07-13 21:36:20 +08:00

7.6 KiB
Raw Permalink Blame History

变异模式

本参考文档涵盖了编写高效 GraphQL 变异的模式。

目录

变异结构

基础变异

mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
    createdAt
  }
}

变量:

{
  "input": {
    "title": "My Post",
    "content": "Post content..."
  }
}

带多个参数的变异

mutation UpdatePost($id: ID!, $input: UpdatePostInput!) {
  updatePost(id: $id, input: $input) {
    id
    title
    updatedAt
  }
}

多个变异

在单个请求中执行多个变异(顺序执行):

mutation SetupUserProfile($userId: ID!, $profileInput: ProfileInput!, $settingsInput: SettingsInput!) {
  updateProfile(userId: $userId, input: $profileInput) {
    id
    bio
  }
  updateSettings(userId: $userId, input: $settingsInput) {
    id
    theme
    notifications
  }
}

输入模式

单一输入对象

推荐模式——单一输入参数:

mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    id
    email
  }
}
{
  "input": {
    "email": "user@example.com",
    "name": "John Doe",
    "password": "secret123"
  }
}

嵌套输入对象

mutation CreateOrder($input: CreateOrderInput!) {
  createOrder(input: $input) {
    id
    total
  }
}
{
  "input": {
    "items": [
      { "productId": "prod_1", "quantity": 2 },
      { "productId": "prod_2", "quantity": 1 }
    ],
    "shippingAddress": {
      "street": "123 Main St",
      "city": "New York",
      "country": "US"
    }
  }
}

可选字段

mutation UpdateUser($id: ID!, $input: UpdateUserInput!) {
  updateUser(id: $id, input: $input) {
    id
    name
    bio
  }
}
{
  "id": "user_123",
  "input": {
    "name": "New Name"
    // bio 未包含——不会被更改
  }
}

响应选择

返回修改后的对象

始终返回带有更新字段的变异对象:

mutation UpdatePost($id: ID!, $input: UpdatePostInput!) {
  updatePost(id: $id, input: $input) {
    id
    title
    content
    updatedAt # 服务端设置的字段
  }
}

返回相关对象

如果变异影响了关联数据,请包含它:

mutation AddComment($input: AddCommentInput!) {
  addComment(input: $input) {
    id
    body
    post {
      id
      commentCount # 更新后的计数
    }
    author {
      id
      name
    }
  }
}

为缓存更新返回

选择更新缓存所需的字段:

mutation DeletePost($id: ID!) {
  deletePost(id: $id) {
    id # 需要从缓存中移除
    author {
      id
      postCount # 可能需要递减
    }
  }
}

为列表更新返回连接

mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
    createdAt
    author {
      id
      posts(first: 1) {
        edges {
          node {
            id
          }
        }
        totalCount
      }
    }
  }
}

错误处理

查询结果联合类型

当 schema 使用联合类型来表示错误时:

mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    ... on CreateUserSuccess {
      user {
        id
        email
      }
    }
    ... on ValidationError {
      message
      field
    }
    ... on EmailAlreadyExists {
      message
      existingUserId
    }
  }
}

处理所有情况

const result = await client.mutate({
  mutation: CREATE_USER,
  variables: { input },
});

const { createUser } = result.data;

switch (createUser.__typename) {
  case "CreateUserSuccess":
    // 处理成功
    return createUser.user;
  case "ValidationError":
    // 处理验证错误
    throw new ValidationError(createUser.field, createUser.message);
  case "EmailAlreadyExists":
    // 处理特定的业务错误
    throw new EmailExistsError(createUser.existingUserId);
}

GraphQL 错误

处理网络错误和 GraphQL 错误:

try {
  const result = await client.mutate({
    mutation: CREATE_POST,
    variables: { input },
  });
  return result.data.createPost;
} catch (error) {
  if (error.graphQLErrors?.length) {
    // 处理 GraphQL 错误
    const gqlError = error.graphQLErrors[0];
    if (gqlError.extensions?.code === "UNAUTHENTICATED") {
      // 重定向到登录页
    }
  }
  if (error.networkError) {
    // 处理网络错误
  }
  throw error;
}

乐观更新

为乐观响应选择字段

包含所有将立即显示的字段:

mutation LikePost($postId: ID!) {
  likePost(postId: $postId) {
    id
    likeCount
    isLikedByViewer
  }
}
client.mutate({
  mutation: LIKE_POST,
  variables: { postId: "post_123" },
  optimisticResponse: {
    likePost: {
      __typename: "Post",
      id: "post_123",
      likeCount: currentCount + 1,
      isLikedByViewer: true,
    },
  },
});

包含创建的 ID

对于创建型变异,使用临时 ID

mutation AddComment($input: AddCommentInput!) {
  addComment(input: $input) {
    id
    body
    createdAt
    author {
      id
      name
      avatarUrl
    }
  }
}
client.mutate({
  mutation: ADD_COMMENT,
  variables: { input: { postId, body } },
  optimisticResponse: {
    addComment: {
      __typename: "Comment",
      id: `temp-${Date.now()}`, // 临时 ID
      body,
      createdAt: new Date().toISOString(),
      author: {
        __typename: "User",
        id: currentUser.id,
        name: currentUser.name,
        avatarUrl: currentUser.avatarUrl,
      },
    },
  },
});

变异命名

命名约定

操作 模式 示例
创建(Create Create{Type} CreateUser, CreatePost
更新(Update Update{Type} UpdateUser, UpdatePost
删除(Delete Delete{Type} DeleteUser, DeletePost
操作(Action {Verb}{Type} PublishPost, ArchiveProject
关系(Relationship {Add/Remove}{Type} AddTeamMember, RemoveTag

好的命名示例

mutation CreateUser($input: CreateUserInput!) { ... }
mutation UpdateUserProfile($userId: ID!, $input: ProfileInput!) { ... }
mutation DeletePost($id: ID!) { ... }
mutation PublishArticle($id: ID!) { ... }
mutation ArchiveProject($id: ID!) { ... }
mutation AddItemToCart($input: AddItemInput!) { ... }
mutation RemoveTeamMember($teamId: ID!, $userId: ID!) { ... }
mutation FollowUser($userId: ID!) { ... }
mutation MarkNotificationAsRead($id: ID!) { ... }

操作名称与服务端一致

使客户端操作名称与服务端变异名称一致:

# 服务端 schema
type Mutation {
  createPost(input: CreatePostInput!): Post!
}

# 客户端操作——名称反映其行为
mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
  }
}

上下文特定的名称

当同一个变异用于不同场景时,添加上下文:

# 用于创建草稿
mutation CreateDraftPost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
    status
  }
}

# 用于创建并立即发布
mutation CreateAndPublishPost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
    status
    publishedAt
  }
}