GraphQLのミューテーションとサブスクリプション
この記事では、Mutation
およびSubscription
タイプを使用して、GraphQLでクエリを実行するだけでなく、データを操作して変更を監視する方法について説明します。 公式ドキュメントで詳細をお気軽に見つけてください。
簡単にするために、データベースやHTTPリクエストは使用しませんが、スキーマとリゾルバーを使用して基本的なAPIを設定する方法を知っている必要があります。
インストール
サーバーをセットアップするためにgraphql-yogaライブラリを使用し、サーバーを自動的にリロードするためにnodemon
を使用します。 また、JavaScriptの最新機能を使用できるように、Preprosやbabelなどのプリプロセッサーも必要です。
$ npm i graphql-yoga nodemon
ボイラープレートの設定
サーバーのセットアップに加えて、空のusers
配列と、すべてのユーザーを返すための単純なスキーマとリゾルバーがあります。
server.js
import { GraphQLServer } from 'graphql-yoga' const users = []; const typeDefs = ` type Query { users: [User!]! } type User { name: String! age: Int! } `; const resolvers = { Query: { user() { return users; } } } const server = new GraphQLServer({ typeDefs, resolvers }); server.start(() => console.log('server running'));
出力ファイルでnodemonを実行するstart
スクリプトが必要です。
package.json
{ "name": "graphql-api", "version": "1.0.0", "description": "", "main": "server.js", "dependencies": { "graphql-yoga": "^1.16.7" }, "devDependencies": { "nodemon": "^1.19.1" }, "scripts": { "start": "nodemon server-dist.js" }, "author": "", "license": "ISC" }
これで、ターミナルでnpm run start
を実行できます。
localhost:4000
で、 GraphQL Playground を起動して実行し、user { name }
のクエリで空の配列を返す必要があります。
ミューテーションを作成する
ミューテーションの構文は、クエリの構文とほぼ同じです。 必要なオプションを宣言し、引数(存在する場合)を追加し、完了時に返されるタイプを宣言するだけです。
すべての引数をインラインで追加する代わりに、整理のためにデータをinput
タイプと呼ばれる独自の特殊なタイプに分割するのが一般的です。 これは、 Prisma などのツールで見られる一般的な命名規則であり、リゾルバーがinputという単語で終わるものに関係なく入力に名前を付けるため、addUser
はAddUserInput
入力を取得します。
server.js
const typeDefs = ` type Mutation { addUser(data: AddUserInput): User! } input AddUserInput { name: String!, age: Int! } `;
クエリの場合と同様に、args
の引数にアクセスし、新しいユーザーを配列に追加して、それらを返すことができます。
const resolvers = { Query: {...}, Mutation: { addUser(parent, args, ctx, info) { const user = { ...args.data }; users.push(user); return user; } } }
ミューテーションの削除と更新
構文が非常に単純なため、他のCRUD操作を具体化するのはほとんど簡単です。
名前でユーザーを検索するだけで、削除または更新するアイテムがわかります。
server.js
const typeDefs = ` type Mutation { deleteUser(name: String!): User! updateUser(name: String!, data: UpdateUserInput): User! } input UpdateUserInput { name: String age: Int } ` const resolvers = { Query: { ... }, Mutation: { deleteUser(parent, args, ctx, info) { // We're just finding the index of the user with a matching name, // checking if it exists, and removing that section of the array. const userIndex = users.findIndex(user => user.name.toLowerCase() === args.name.toLowerCase()); if (userIndex === -1) throw new Error('User not found'); const user = users.splice(userIndex, 1); return user[0]; }, updateUser(parent, args, ctx, info) { const user = users.find(user => user.name.toLowerCase() === args.who.toLowerCase()); if (!user) throw new Error('User not found'); // This way, only the fields that are passed-in will be changed. if (typeof args.data.name === "string") user.name = args.data.name; if (typeof args.data.age !== "undefined") user.age = args.data.age; return user; } } }
localhost:4000
で、このミューテーションを試して、配列を再度クエリできます。
mutation { addUser(data: { name: "Alli", age: 48 }) { name age } }
または別のタブで:
mutation { updateUser(name: "Alli", data: { name: "Crusher", age: 27 }) { name age } }
サブスクリプション
特別なSubscription
タイプを使用して、データの変更を監視できます。 構文はクエリやミューテーションの構文と非常によく似ています。タイプSubscription
を追加し、監視したいものを追加して、返したいものを追加するだけです。 変更されたデータと、それが作成、削除、または更新のいずれの操作であったかを通知するカスタムタイプを返します。
サブスクリプションを使用するには、graphql-yogaのPubSub
を使用し、何よりも先に初期化する必要があります。 サブスクリプションリゾルバーでは、subscribe
という関数を使用します。この関数は、user
という名前の非同期イベントを返す必要があります。 このサブスクリプションに何かを接続する場合は常に、このイベント名を使用します。
server.js
import { GraphQLServer, PubSub } from 'graphql-yoga'; const pubsub = new PubSub(); const typeDefs = ` type Subscription { user: UserSubscription! } type UserSubscription { mutation: String! data: User! } ` const resolvers = { Query: { ... }, Mutation: { ... }, Subscription: { user: { subscribe() { return pubsub.asyncIterator('user'); } } } }
サブスクリプション自体はセットアップされており、生成されたGraphQLドキュメントで利用できますが、いつ起動するか、何を返すかはわかりません。 ミューテーションに戻って、pubsub.publish
を追加して、user
イベントにリンクし、データを返します。
const resolvers = { Query: { ... }, Mutation: { addUser(parent, args, ctx, info) { const user = { ...args.data }; users.push(user); // We'll just link it to our user event, // and return what type of mutation this is and our new user. pubsub.publish("user", { user: { mutation: "Added", data: user } }); return user; }, deleteUser(parent, args, ctx, info) { const userIndex = users.findIndex( user => user.name.toLowerCase() === args.who.toLowerCase() ); if (userIndex === -1) throw new Error("User not found"); const user = users.splice(userIndex, 1); pubsub.publish("user", { user: { mutation: "Deleted", data: user[0] } }); return user[0]; }, updateUser(parent, args, ctx, info) { const user = users.find( user => user.name.toLowerCase() === args.who.toLowerCase() ); if (!user) throw new Error("User not found"); if (typeof args.data.name === "string") user.name = args.data.name; if (typeof args.data.age !== "undefined") user.age = args.data.age; pubsub.publish("user", { user: { mutation: "Updated", data: user } }); return user; } }, Subscription: { ... } };
localhost:4000
で、新しいタブを開いて次のサブスクリプションを実行できます。 小さな糸車で「聞いている…」というメッセージが表示されるはずです。 別のタブで、他の過去のミューテーションを実行できるようになりました。サブスクリプションは、実行された内容と変更された内容を自動的に返します。
subscription { user { mutation data { name age } } }
結論
これが、ミューテーションとサブスクリプションを使用してGraphQLAPIをセットアップする方法を理解するのに役立つことを願っています。 これを設定する際に問題が発生した場合は、いつでもこのリポジトリを確認できます。