如果你有一个更改数据的 API 端点,例如将数据插入数据库或更改数据库中已有的数据,则应将此端点设置为 Mutation
而不是 Query
。这就像将 API 端点作为顶层 Mutation
类型而不是顶层 Query
类型的一部分一样简单。
¥If you have an API endpoint that alters data, like inserting data into a database or altering data already in a database, you should make this endpoint a Mutation
rather than a Query
. This is as simple as making the API endpoint part of the top-level Mutation
type instead of the top-level Query
type.
假设我们有一个“每日消息”服务器,任何人都可以更新每日消息,并且任何人都可以阅读当前消息。GraphQL 结构很简单:
¥Let’s say we have a “message of the day” server, where anyone can update the message of the day, and anyone can read the current one. The GraphQL schema for this is simply:
type Mutation {
setMessage(message: String): String
}
type Query {
getMessage: String
}
映射到数据库创建或更新操作的变更通常很方便,例如 setMessage
,返回服务器存储的相同内容。这样,如果你修改服务器上的数据,客户端就可以了解这些修改。
¥It’s often convenient to have a mutation that maps to a database create or update operation, like setMessage
, return the same thing that the server stored. That way, if you modify the data on the server, the client can learn about those modifications.
变更和查询都可以由根解析器处理,因此实现此结构的根可以简单地是:
¥Both mutations and queries can be handled by root resolvers, so the root that implements this schema can simply be:
var fakeDatabase = {}
var root = {
setMessage({ message }) {
fakeDatabase.message = message
return message
},
getMessage() {
return fakeDatabase.message
},
}
除此之外,你不需要任何其他东西来实现变更。但在许多情况下,你会发现许多不同的变更都接受相同的输入参数。一个常见的例子是,在数据库中创建对象和更新数据库中的对象通常采用相同的参数。为了使你的结构更简单,你可以使用“输入类型”,即使用 input
关键字而不是 type
关键字。
¥You don’t need anything more than this to implement mutations. But in many cases, you will find a number of different mutations that all accept the same input parameters. A common example is that creating an object in a database and updating an object in a database often take the same parameters. To make your schema simpler, you can use “input types” for this, by using the input
keyword instead of the type
keyword.
例如,假设我们有许多消息,在数据库中按 id
字段索引,并且每条消息都有 content
字符串和 author
字符串,而不是当天的一条消息。我们需要一个变更 API 来创建新消息和更新旧消息。我们可以使用以下结构:
¥For example, instead of a single message of the day, let’s say we have many messages, indexed in a database by the id
field, and each message has both a content
string and an author
string. We want a mutation API both for creating a new message and for updating an old message. We could use the schema:
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
的更多信息。
¥Here, the mutations return a Message
type, so that the client can get more information about the newly-modified Message
in the same request as the request that mutates it.
输入类型不能具有其他对象的字段,只能具有基本标量类型、列表类型和其他输入类型。
¥Input types can’t have fields that are other objects, only basic scalar types, list types, and other input types.
以 Input
结尾的命名输入类型是一个有用的约定,因为对于单个概念对象,你通常需要略有不同的输入类型和输出类型。
¥Naming input types with Input
on the end is a useful convention, because you will often want both an input type and an output type that are slightly different for a single conceptual object.
下面是一些实现此结构的可运行代码,将数据保存在内存中:
¥Here’s some runnable code that implements this schema, keeping the data in memory:
var express = require("express")
var { createHandler } = require("graphql-http/lib/use/express")
var { buildSchema } = require("graphql")
// Construct a schema, using GraphQL schema language
var schema = buildSchema(/* GraphQL */`
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 content
var 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")
})
要调用变更,你必须在 GraphQL 查询之前使用关键字 mutation
。要传递输入类型,请提供写入的数据,就好像它是 JSON 对象一样。例如,使用上面定义的服务器,你可以通过以下操作创建一条新消息并返回新消息的 id
:
¥To call a mutation, you must use the keyword mutation
before your GraphQL query. To pass an input type, provide the data written as if it’s a JSON object. For example, with the server defined above, you can create a new message and return the id
of the new message with this operation:
mutation {
createMessage(input: {
author: "andy",
content: "hope is a good thing",
}) {
id
}
}
你可以使用变量来简化变更客户端逻辑,就像使用查询一样。例如,调用服务器执行此变更的一些 JavaScript 代码是:
¥You can use variables to simplify mutation client logic just like you can with queries. For example, some JavaScript code that calls the server to execute this mutation is:
var author = "andy"
var content = "hope is a good thing"
var query = /* GraphQL */`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))
一种特殊类型的变更是更改用户的操作,例如注册新用户。虽然你可以使用 GraphQL 变更来实现这一点,但如果你了解 具有身份验证和 Express 中间件的 GraphQL,则可以重用许多现有库。
¥One particular type of mutation is operations that change users, like signing up a new user. While you can implement this using GraphQL mutations, you can reuse many existing libraries if you learn about GraphQL with authentication and Express middleware.