如果您有一個會變更資料的 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 },}
您不需要比這更多來實作突變。但在許多情況下,您會發現許多不同的突變都接受相同的輸入參數。一個常見的範例是,在資料庫中建立物件和更新資料庫中的物件通常需要相同的參數。為了簡化您的規範,您可以使用「輸入類型」,方法是使用 input
關鍵字,而不是 type
關鍵字。
例如,假設我們不是只有單一每日訊息,而是有許多訊息,並以資料庫中的 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")})
若要呼叫變異,你必須在 GraphQL 查詢之前使用關鍵字 mutation
。若要傳遞輸入類型,請提供以 JSON 物件形式撰寫的資料。例如,使用上述定義的伺服器,你可以建立一則新訊息,並透過這個操作傳回新訊息的 id
mutation { createMessage(input: { author: "andy", content: "hope is a good thing", }) { id }}
你可以使用變數來簡化變異用戶端邏輯,就像你使用查詢一樣。例如,呼叫伺服器執行這個變異的 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))
變異的其中一種特定類型是會變更使用者的操作,例如註冊新使用者。雖然你可以使用 GraphQL 變異來實作這個功能,但如果你瞭解 具有驗證和 Express 中介軟體的 GraphQL,你可以重複使用許多現有的函式庫。