[Rails]GraphQLのmutationについて
はじめに
前回の記事の続きでGraphQLのmutationについてまとめていきます。
セットアップ
GraphQLのmutation
mutationは、GraphQLでデータを変更するための操作を定義するための機能です。
クライアントがサーバーにデータの変更を要求する場合、通常はmutationを使用します。
mutationは、データの作成、更新、削除などの変更操作を実行するためのエンドポイントを提供します。
一般的に、GraphQLスキーマ内のmutationフィールドには、クライアントが実行できるさまざまなmutation操作が定義されます。例えば、ユーザーの作成、更新、削除などが一般的なMutation操作です。
各mutation操作は、対応する入力を受け取り、必要に応じて結果を返します。例えば、createUser
Mutationは、CreateUserInput
という入力を受け取り、新しく作成されたユーザーオブジェクトを返します。
クライアントは、GraphQLクエリを使用してこれらのmutation操作を呼び出し、サーバー上でデータの変更を実行します。
tl;dr
- mutationタイプを定義する
-
createUser
mutationを作成する - 検証する
-
updateUser
mutationを作成する -
deleteUser
mutationを作成する
簡単なユーザーmutationを定義し、動作確認を行なっていきます。
mutationタイプを定義する
GraphQLスキーマ定義ファイル(通常はapp/graphql/types/mutation_type.rb
)で、mutationタイプとcreate_user
フィールドを定義します。
ファイルはrails generate graphql:install
ジェネレーターコマンドに
によって作成されたものです。
module Types
class MutationType < BaseObject
field :create_user, mutation: Mutations::User::CreateUser
end
end
mutationクラスを作成する
app/graphql/mutations
ディレクトリに新しいmutationクラスを作成します。
ユーザー周りのmutationを分かりやすくまとめたいので/user
ディレクトリも作成します。
このクラスはMutations::BaseMutation
を継承し、入力フィールド、リターン・タイプ、リゾルバ・メソッドを定義します。
リゾルバに指定された名前とメールアドレスで新しいユーザーを作成し、そのユーザーまたは作成プロセス中に発生した検証エラーを返すように定義します。
module Mutation
class Mutations::User::CreateUser < Mutations::BaseMutation
argument :name, String, required: true
argument :email, String, required: true
field :user, Types::UserType, null: false
field :errors, [String], null: false
def resolve(name:, email:)
user = User.new(name:, email:)
if user.save
{user: user, errors: [] }
else
{ user: nil, errors: user.errors.full_messages }
end
end
end
end
検証
早速ユーザーの作成を検証していきます。
また、コンソールにてユーザーが作成されたことも確認します。
irb(main):001> User.last
User Load (2.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
=>
#<User:0x00007fe2c19ab1f8
id: 12,
name: "Test User",
email: "user@example.com",
created_at: Sun, 25 Feb 2024 13:45:09.244542000 UTC +00:00,
updated_at: Sun, 25 Feb 2024 13:45:09.244542000 UTC +00:00>
updateUser
mutationを作成する
ユーザーを更新するmutationも定義していきます。
リゾルバにユーザーIDからユーザーを取得し、更新されたユーザーまたは更新プロセス中に発生した検証エラーを返すように定義します。
module Mutation
class Mutations::User::UpdateUser < Mutations::BaseMutation
argument :id, ID, required: true
argument :name, String, required: true
argument :email, String, required: true
field :user, Types::UserType, null: true
field :errors, [String], null: false
def resolve(id:, name:, email:)
user = User.find_by(id: id)
return { user: nil, errors: ['User not found'] } if user.nil?
if user.update(name: name, email: email)
{ user: user, errors: [] }
else
{ user: nil, errors: user.errors.full_messages }
end
end
end
end
mutationタイプにupdate_user
フィールドを追加します。
module Types
class MutationType < BaseObject
field :update_user, mutation: Mutations::User::UpdateUser
end
end
先ほど作成したユーザーを更新してみます。
input
というキーワードを使って入力することも可能です。
input
に対する値は、クエリを実行する際に、外部から渡す必要があります。
DB上にも更新されたことを確認します。
#<User:0x00007fe2c2167450
id: 12,
name: "Updated Name",
email: "user@example.com",
created_at: Sun, 25 Feb 2024 13:45:09.244542000 UTC +00:00,
updated_at: Sun, 25 Feb 2024 14:00:23.963120000 UTC +00:00>
ユーザーの更新に失敗した場合も確認します。
deleteUser
mutationを作成する
最後にユーザーを削除するdeleteUser
mutationを定義します。
module Mutation
class Mutations::User::DeleteUser < Mutations::BaseMutation
argument :id, ID, required: true
field :user, Types::UserType, null: true
field :errors, [String], null: false
def resolve(id:)
user = User.find_by(id: id)
return { user: nil, errors: ['User not found'] } if user.nil?
if user.destroy
{ user: user, errors: [''] }
else
{ user: nil, errors: user.errors.full_messages }
end
end
end
end
mutationタイプにdelete_user
フィールドを追加します。
module Types
class MutationType < BaseObject
field :delete_user, mutation: Mutations::User::DeleteUser
end
end
ユーザーの削除を検証します。
ユーザーが存在しない場合エラーを返します。
終わり
簡単ですがGraphQLのmutationについてまとめてみました。
複数の変更を行いたい場合でも、それぞれの変更に対して独立したミューテーションを定義することで、エンドポイントの使用を単純化し、コードを維持しやすくなるのでそこを意識していきたいです。
Discussion