GraphQL 变更操作 #

变更基础 #

Mutation 是 GraphQL 中用于修改数据的操作类型,类似于 REST API 中的 POST、PUT、DELETE 请求。

text
┌─────────────────────────────────────────────────────────────┐
│                    Mutation 操作特点                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ✅ 写入操作:修改服务器数据                                  │
│  ✅ 顺序执行:多个变更按顺序执行                              │
│  ✅ 返回数据:可返回修改后的数据                              │
│  ✅ 原子操作:支持事务性变更                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Query vs Mutation #

text
┌─────────────────────────────────────────────────────────────┐
│                    Query 与 Mutation 对比                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Query:                                                    │
│  - 只读操作                                                  │
│  - 并行执行                                                  │
│  - 可缓存                                                    │
│  - 幂等性                                                    │
│                                                             │
│  Mutation:                                                 │
│  - 写入操作                                                  │
│  - 顺序执行                                                  │
│  - 不应缓存                                                  │
│  - 可能非幂等                                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

创建操作 #

基本创建 #

graphql
mutation {
  createUser(name: "Alice", email: "alice@example.com") {
    id
    name
    email
    createdAt
  }
}

响应:

json
{
  "data": {
    "createUser": {
      "id": "1",
      "name": "Alice",
      "email": "alice@example.com",
      "createdAt": "2024-01-15T10:30:00Z"
    }
  }
}

使用输入对象 #

复杂的数据结构使用输入对象更清晰:

graphql
mutation {
  createUser(input: {
    name: "Alice"
    email: "alice@example.com"
    age: 25
    role: "user"
    preferences: {
      theme: "dark"
      language: "zh-CN"
    }
  }) {
    id
    name
    email
    age
    role
    preferences {
      theme
      language
    }
  }
}

使用变量 #

graphql
mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    id
    name
    email
  }
}

变量:

json
{
  "input": {
    "name": "Alice",
    "email": "alice@example.com",
    "age": 25
  }
}

批量创建 #

graphql
mutation {
  createUsers(inputs: [
    { name: "Alice", email: "alice@example.com" },
    { name: "Bob", email: "bob@example.com" },
    { name: "Charlie", email: "charlie@example.com" }
  ]) {
    id
    name
    email
  }
}

更新操作 #

基本更新 #

graphql
mutation {
  updateUser(id: "1", name: "New Name") {
    id
    name
    updatedAt
  }
}

使用输入对象 #

graphql
mutation UpdateUser($id: ID!, $input: UpdateUserInput!) {
  updateUser(id: $id, input: $input) {
    id
    name
    email
    age
    updatedAt
  }
}

变量:

json
{
  "id": "1",
  "input": {
    "name": "New Name",
    "age": 26
  }
}

部分更新 #

只更新提供的字段:

graphql
mutation {
  updateUser(id: "1", input: { name: "Updated Name" }) {
    id
    name
    email
    age
  }
}

条件更新 #

graphql
mutation {
  updateUser(
    id: "1"
    input: { status: "inactive" }
    expectedVersion: 5
  ) {
    id
    status
    version
  }
}

批量更新 #

graphql
mutation {
  updateUsers(
    filter: { status: { eq: "pending" } }
    input: { status: "active" }
  ) {
    affectedCount
    users {
      id
      status
    }
  }
}

删除操作 #

基本删除 #

graphql
mutation {
  deleteUser(id: "1") {
    id
    success
    message
  }
}

软删除 #

graphql
mutation {
  softDeleteUser(id: "1") {
    id
    deletedAt
    status
  }
}

批量删除 #

graphql
mutation {
  deleteUsers(filter: { status: { eq: "inactive" } }) {
    affectedCount
    deletedIds
  }
}

级联删除 #

graphql
mutation {
  deleteUser(id: "1", cascade: true) {
    id
    deletedPosts {
      id
      title
    }
    deletedComments {
      id
    }
  }
}

输入类型设计 #

创建输入类型 #

graphql
input CreateUserInput {
  name: String!
  email: String!
  age: Int
  role: UserRole = USER
  preferences: PreferencesInput
}

input PreferencesInput {
  theme: String = "light"
  language: String = "en"
  notifications: Boolean = true
}

使用:

graphql
mutation {
  createUser(input: {
    name: "Alice"
    email: "alice@example.com"
    preferences: {
      theme: "dark"
      language: "zh-CN"
    }
  }) {
    id
    name
  }
}

更新输入类型 #

graphql
input UpdateUserInput {
  name: String
  email: String
  age: Int
  role: UserRole
  status: UserStatus
  preferences: PreferencesInput
}

嵌套输入类型 #

graphql
input CreatePostInput {
  title: String!
  content: String!
  excerpt: String
  status: PostStatus = DRAFT
  tags: [String!]
  category: CategoryInput
  seo: SEOInput
}

input CategoryInput {
  id: ID
  name: String
  slug: String
}

input SEOInput {
  title: String
  description: String
  keywords: [String!]
}

输入类型最佳实践 #

text
┌─────────────────────────────────────────────────────────────┐
│                    输入类型设计原则                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 分离创建和更新输入                                       │
│     - CreateInput:必填字段多                                │
│     - UpdateInput:所有字段可选                              │
│                                                             │
│  2. 使用嵌套输入对象                                         │
│     - 组织复杂数据结构                                       │
│     - 提高可读性                                             │
│                                                             │
│  3. 提供默认值                                               │
│     - 常用字段设置默认值                                     │
│     - 减少客户端负担                                         │
│                                                             │
│  4. 命名规范                                                 │
│     - 输入类型以 Input 结尾                                  │
│     - 操作以动词开头                                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

返回类型设计 #

返回创建的对象 #

graphql
mutation {
  createUser(input: { name: "Alice", email: "alice@example.com" }) {
    id
    name
    email
    createdAt
  }
}

返回负载类型 #

graphql
type UserMutationPayload {
  user: User
  success: Boolean!
  message: String
  errors: [Error!]
}

type Error {
  field: String!
  message: String!
  code: String
}

使用:

graphql
mutation {
  createUser(input: { name: "Alice", email: "alice@example.com" }) {
    user {
      id
      name
      email
    }
    success
    message
    errors {
      field
      message
      code
    }
  }
}

返回关联数据 #

graphql
mutation {
  createPost(input: { title: "Hello", content: "World" }) {
    post {
      id
      title
      content
      author {
        id
        name
      }
      comments {
        id
        content
      }
    }
  }
}

错误处理 #

错误响应格式 #

json
{
  "data": {
    "createUser": null
  },
  "errors": [
    {
      "message": "Email already exists",
      "locations": [{ "line": 2, "column": 3 }],
      "path": ["createUser"],
      "extensions": {
        "code": "DUPLICATE_EMAIL",
        "field": "email"
      }
    }
  ]
}

业务错误处理 #

graphql
mutation {
  createUser(input: { name: "Alice", email: "invalid-email" }) {
    user {
      id
      name
    }
    success
    errors {
      field
      message
      code
    }
  }
}

响应:

json
{
  "data": {
    "createUser": {
      "user": null,
      "success": false,
      "errors": [
        {
          "field": "email",
          "message": "Invalid email format",
          "code": "VALIDATION_ERROR"
        }
      ]
    }
  }
}

错误类型定义 #

graphql
type MutationResponse {
  success: Boolean!
  message: String
  errors: [FieldError!]
}

type FieldError {
  field: String!
  message: String!
  code: ErrorCode
}

enum ErrorCode {
  VALIDATION_ERROR
  NOT_FOUND
  UNAUTHORIZED
  FORBIDDEN
  CONFLICT
  INTERNAL_ERROR
}

多个变更 #

顺序执行 #

GraphQL 保证多个变更按顺序执行:

graphql
mutation {
  createPost(input: { title: "Post 1" }) {
    id
    title
  }
  createPost(input: { title: "Post 2" }) {
    id
    title
  }
  createPost(input: { title: "Post 3" }) {
    id
    title
  }
}

使用别名 #

graphql
mutation {
  firstPost: createPost(input: { title: "First" }) {
    id
    title
  }
  secondPost: createPost(input: { title: "Second" }) {
    id
    title
  }
}

关联变更 #

graphql
mutation {
  createUser(input: { name: "Alice", email: "alice@example.com" }) {
    id
    name
  }
  createPost(input: { title: "First Post", authorId: "1" }) {
    id
    title
    author {
      name
    }
  }
}

事务处理 #

原子操作 #

graphql
mutation TransferMoney($from: ID!, $to: ID!, $amount: Float!) {
  transferMoney(from: $from, to: $to, amount: $amount) {
    success
    fromAccount {
      id
      balance
    }
    toAccount {
      id
      balance
    }
    transaction {
      id
      amount
      createdAt
    }
  }
}

批量事务 #

graphql
mutation BatchUpdate($operations: [BatchOperation!]!) {
  batchUpdate(operations: $operations) {
    success
    results {
      operation
      success
      data {
        ... on User {
          id
          name
        }
        ... on Post {
          id
          title
        }
      }
      error {
        message
        code
      }
    }
  }
}

文件上传 #

单文件上传 #

graphql
mutation UploadFile($file: Upload!) {
  uploadFile(file: $file) {
    id
    filename
    mimetype
    size
    url
  }
}

多文件上传 #

graphql
mutation UploadFiles($files: [Upload!]!) {
  uploadFiles(files: $files) {
    id
    filename
    url
  }
}

带数据的文件上传 #

graphql
mutation CreatePostWithImage($input: CreatePostInput!, $image: Upload!) {
  createPostWithImage(input: $input, image: $image) {
    id
    title
    content
    coverImage {
      id
      url
      width
      height
    }
  }
}

变更最佳实践 #

1. 命名规范 #

graphql
type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): DeleteResult!
  
  createPost(input: CreatePostInput!): Post!
  updatePost(id: ID!, input: UpdatePostInput!): Post!
  deletePost(id: ID!): DeleteResult!
}

2. 使用输入类型 #

graphql
mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    id
    name
  }
}

3. 返回有意义的数据 #

graphql
mutation {
  createPost(input: { title: "Hello" }) {
    id
    title
    slug
    author {
      id
      name
    }
    createdAt
  }
}

4. 提供操作结果 #

graphql
mutation {
  deletePost(id: "1") {
    success
    message
    deletedId
  }
}

5. 支持乐观更新 #

graphql
mutation UpdateUser($id: ID!, $input: UpdateUserInput!) {
  updateUser(id: $id, input: $input) {
    id
    name
    email
    __typename
  }
}

实战示例 #

示例一:用户注册 #

graphql
mutation Register($input: RegisterInput!) {
  register(input: $input) {
    user {
      id
      name
      email
      avatar
      createdAt
    }
    token
    success
    errors {
      field
      message
      code
    }
  }
}

变量:

json
{
  "input": {
    "name": "Alice",
    "email": "alice@example.com",
    "password": "securePassword123",
    "confirmPassword": "securePassword123"
  }
}

示例二:创建文章 #

graphql
mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
    slug
    content
    excerpt
    status
    coverImage {
      id
      url
    }
    author {
      id
      name
    }
    tags
    categories {
      id
      name
      slug
    }
    seo {
      title
      description
      keywords
    }
    createdAt
    publishedAt
  }
}

变量:

json
{
  "input": {
    "title": "GraphQL 入门教程",
    "content": "这是一篇关于 GraphQL 的入门教程...",
    "excerpt": "学习 GraphQL 的基础知识",
    "status": "PUBLISHED",
    "tags": ["GraphQL", "API", "Tutorial"],
    "categoryIds": ["1", "2"],
    "seo": {
      "title": "GraphQL 入门教程 - 完整指南",
      "description": "从零开始学习 GraphQL",
      "keywords": ["GraphQL", "API"]
    }
  }
}

示例三:更新用户资料 #

graphql
mutation UpdateProfile($input: UpdateProfileInput!) {
  updateProfile(input: $input) {
    id
    name
    email
    avatar
    bio
    location
    website
    socialLinks {
      twitter
      github
      linkedin
    }
    preferences {
      theme
      language
      notifications
    }
    updatedAt
  }
}

示例四:批量操作 #

graphql
mutation BatchOperation($operations: [BatchOperationInput!]!) {
  batchOperation(operations: $operations) {
    success
    results {
      id
      operation
      success
      data {
        ... on User {
          id
          name
          status
        }
        ... on Post {
          id
          title
          status
        }
      }
      error {
        message
        code
      }
    }
    summary {
      total
      successful
      failed
    }
  }
}

变量:

json
{
  "operations": [
    {
      "type": "UPDATE_USER",
      "id": "1",
      "data": { "status": "active" }
    },
    {
      "type": "UPDATE_USER",
      "id": "2",
      "data": { "status": "active" }
    },
    {
      "type": "DELETE_POST",
      "id": "100"
    }
  ]
}

示例五:评论系统 #

graphql
mutation CreateComment($input: CreateCommentInput!) {
  createComment(input: $input) {
    id
    content
    status
    author {
      id
      name
      avatar
    }
    post {
      id
      title
      commentCount
    }
    parent {
      id
      content
    }
    replies {
      id
      content
    }
    createdAt
  }
}

安全考虑 #

输入验证 #

graphql
input CreateUserInput {
  name: String! @constraint(minLength: 2, maxLength: 50)
  email: String! @constraint(format: "email")
  age: Int @constraint(min: 0, max: 150)
  password: String! @constraint(minLength: 8, pattern: "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]+$")
}

权限控制 #

graphql
mutation {
  deleteUser(id: "1") {
    id
    success
  }
}

服务端验证:

javascript
const resolvers = {
  Mutation: {
    deleteUser: async (_, { id }, context) => {
      if (!context.user) {
        throw new AuthenticationError('Not authenticated');
      }
      
      if (context.user.role !== 'admin') {
        throw new ForbiddenError('Not authorized');
      }
      
      return deleteUser(id);
    }
  }
};

防止批量滥用 #

graphql
type Mutation {
  createUsers(inputs: [CreateUserInput!]! @validate(maxLength: 10)): [User!]!
}

下一步 #

现在你已经掌握了 GraphQL 变更操作的各种技巧,接下来学习 Schema 定义,了解如何设计 GraphQL API!

最后更新:2026-03-29