如果您有一个更改数据的 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,你可以重用许多现有的库。