如果您有一个更改数据的 API 端点,例如将数据插入数据库或更改数据库中已有的数据,您应该将其端点设为 Mutation 而不是 Query。这很简单,只需将 API 端点作为顶层 Mutation 类型的一部分,而不是顶层 Query 类型的一部分。
假设我们有一个“每日消息”服务器,任何人都可以更新每日消息,任何人都可以读取当前消息。此服务器的 GraphQL 架构很简单
type Mutation {  setMessage(message: String): String}
type Query {  getMessage: String}
通常,让一个与数据库创建或更新操作相对应的突变(例如 setMessage)返回服务器存储的相同内容会很方便。这样,如果您修改了服务器上的数据,客户端就可以了解这些修改。
突变和查询都可以由根解析器处理,因此实现此架构的根可以简单地是
var fakeDatabase = {}var root = {  setMessage: ({ message }) => {    fakeDatabase.message = message    return message  },  getMessage: () => {    return fakeDatabase.message  },}
您不需要比这更多的东西来实现突变。但在许多情况下,您会发现许多不同的突变都接受相同的输入参数。一个常见的例子是,在数据库中创建对象和更新数据库中的对象通常需要相同的参数。为了使您的架构更简单,您可以使用“输入类型”来实现这一点,方法是在使用 type 关键字时使用 input 关键字。
例如,假设我们不是只有一个每日消息,而是有许多消息,它们在数据库中按 id 字段索引,每个消息都有一个 content 字符串和一个 author 字符串。我们希望有一个突变 API 用于创建新消息和更新旧消息。我们可以使用以下架构
input MessageInput {  content: String  author: String}
type Message {  id: ID!  content: String  author: String}
type Query {  getMessage(id: ID!): Message}
type Mutation {  createMessage(input: MessageInput): Message  updateMessage(id: ID!, input: MessageInput): Message}
在这里,突变返回 Message 类型,以便客户端可以在与修改它的请求相同的请求中获得有关新修改的 Message 的更多信息。
输入类型不能包含作为其他对象的字段,只能包含基本标量类型、列表类型和其他输入类型。
在类型名称末尾使用Input是一个有用的约定,因为你通常会想要一个输入类型和一个输出类型,它们对于同一个概念对象略有不同。
以下是一些可运行的代码,它实现了这个模式,并将数据保存在内存中
var express = require("express")var { createHandler } = require("graphql-http/lib/use/express")var { buildSchema } = require("graphql")
// Construct a schema, using GraphQL schema languagevar schema = buildSchema(`  input MessageInput {    content: String    author: String  }
  type Message {    id: ID!    content: String    author: String  }
  type Query {    getMessage(id: ID!): Message  }
  type Mutation {    createMessage(input: MessageInput): Message    updateMessage(id: ID!, input: MessageInput): Message  }`)
// If Message had any complex fields, we'd put them on this object.class Message {  constructor(id, { content, author }) {    this.id = id    this.content = content    this.author = author  }}
// Maps username to contentvar fakeDatabase = {}
var root = {  getMessage: ({ id }) => {    if (!fakeDatabase[id]) {      throw new Error("no message exists with id " + id)    }    return new Message(id, fakeDatabase[id])  },  createMessage: ({ input }) => {    // Create a random id for our "database".    var id = require("crypto").randomBytes(10).toString("hex")
    fakeDatabase[id] = input    return new Message(id, input)  },  updateMessage: ({ id, input }) => {    if (!fakeDatabase[id]) {      throw new Error("no message exists with id " + id)    }    // This replaces all old data, but some apps might want partial update.    fakeDatabase[id] = input    return new Message(id, input)  },}
var app = express()app.all(  "/graphql",  createHandler({    schema: schema,    rootValue: root,  }))app.listen(4000, () => {  console.log("Running a GraphQL API server at localhost:4000/graphql")})
要调用一个 mutation,你必须在你的 GraphQL 查询之前使用关键字mutation。要传递一个输入类型,请提供数据,就像它是一个 JSON 对象一样。例如,使用上面定义的服务器,你可以创建一个新消息并使用此操作返回新消息的id
mutation {  createMessage(input: {    author: "andy",    content: "hope is a good thing",  }) {    id  }}
你可以使用变量来简化 mutation 客户端逻辑,就像你可以在查询中使用变量一样。例如,一些调用服务器执行此 mutation 的 JavaScript 代码是
var author = "andy"var content = "hope is a good thing"var query = `mutation CreateMessage($input: MessageInput) {  createMessage(input: $input) {    id  }}`
fetch("/graphql", {  method: "POST",  headers: {    "Content-Type": "application/json",    Accept: "application/json",  },  body: JSON.stringify({    query,    variables: {      input: {        author,        content,      },    },  }),})  .then(r => r.json())  .then(data => console.log("data returned:", data))
一种特殊的 mutation 类型是更改用户的操作,例如注册新用户。虽然你可以使用 GraphQL mutation 来实现这一点,但如果你了解带有身份验证和 Express 中间件的 GraphQL,你可以重用许多现有的库。