GraphQL ロゴGraphQL

ミューテーションと入力型

データベースへのデータ挿入やデータベース内データの変更など、データを変更するAPIエンドポイントがある場合、そのエンドポイントをQueryではなくMutationにする必要があります。これは、APIエンドポイントをトップレベルのQuery型ではなく、トップレベルのMutation型の一部にすることで簡単に実現できます。

例えば、「今日のメッセージ」サーバーがあるとします。誰でも今日のメッセージを更新でき、誰でも現在のメッセージを読むことができます。この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キーワードを使用することによって行います。

例えば、1つの「今日のメッセージ」ではなく、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")
})

ミューテーションを呼び出すには、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))

ミューテーションの特定の1つの型は、新しいユーザーのサインアップなど、ユーザーを変更する操作です。これはGraphQLミューテーションを使用して実装できますが、認証とExpressミドルウェアを使用したGraphQLについて学習すれば、多くの既存のライブラリを再利用できます。

続きを読む →認証とExpressミドルウェア