GraphQL LogoGraphQL

突变和输入类型

如果您有一个更改数据的 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 language
var 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 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")
})

要调用一个 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,你可以重用许多现有的库。

继续阅读 →身份验证和 Express 中间件