🦓

[Rails]GraphQLのmutationについて

2024/02/26に公開

はじめに

前回の記事の続きでGraphQLのmutationについてまとめていきます。
https://graphql.org/learn/queries/#mutations

セットアップ

GraphQLのmutation

mutationは、GraphQLでデータを変更するための操作を定義するための機能です。
クライアントがサーバーにデータの変更を要求する場合、通常はmutationを使用します。
mutationは、データの作成、更新、削除などの変更操作を実行するためのエンドポイントを提供します。

一般的に、GraphQLスキーマ内のmutationフィールドには、クライアントが実行できるさまざまなmutation操作が定義されます。例えば、ユーザーの作成、更新、削除などが一般的なMutation操作です。

各mutation操作は、対応する入力を受け取り、必要に応じて結果を返します。例えば、createUser Mutationは、CreateUserInputという入力を受け取り、新しく作成されたユーザーオブジェクトを返します。

クライアントは、GraphQLクエリを使用してこれらのmutation操作を呼び出し、サーバー上でデータの変更を実行します。

tl;dr

  1. mutationタイプを定義する
  2. createUsermutationを作成する
  3. 検証する
  4. updateUsermutationを作成する
  5. deleteUsermutationを作成する

簡単なユーザーmutationを定義し、動作確認を行なっていきます。

mutationタイプを定義する

GraphQLスキーマ定義ファイル(通常はapp/graphql/types/mutation_type.rb)で、mutationタイプとcreate_userフィールドを定義します。

ファイルはrails generate graphql:installジェネレーターコマンドに
によって作成されたものです。

app/graphql/types/mutation_type.rb
module Types
  class MutationType < BaseObject
    field :create_user, mutation: Mutations::User::CreateUser
  end
end

https://github.com/rmosolgo/graphql-ruby

mutationクラスを作成する

app/graphql/mutationsディレクトリに新しいmutationクラスを作成します。
ユーザー周りのmutationを分かりやすくまとめたいので/userディレクトリも作成します。
このクラスはMutations::BaseMutationを継承し、入力フィールド、リターン・タイプ、リゾルバ・メソッドを定義します。

リゾルバに指定された名前とメールアドレスで新しいユーザーを作成し、そのユーザーまたは作成プロセス中に発生した検証エラーを返すように定義します。

app/graphql/mutations/user/create_user.rb
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>

updateUsermutationを作成する

ユーザーを更新するmutationも定義していきます。

リゾルバにユーザーIDからユーザーを取得し、更新されたユーザーまたは更新プロセス中に発生した検証エラーを返すように定義します。

app/graphql/mutations/user/update_user.rb
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フィールドを追加します。

app/graphql/types/mutation_type.rb
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>

ユーザーの更新に失敗した場合も確認します。

deleteUsermutationを作成する

最後にユーザーを削除するdeleteUsermutationを定義します。

app/graphql/mutations/user/delete_user.rb
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フィールドを追加します。

app/graphql/types/mutation_type.rb
module Types
  class MutationType < BaseObject
    field :delete_user, mutation: Mutations::User::DeleteUser
  end
end

ユーザーの削除を検証します。

ユーザーが存在しない場合エラーを返します。

終わり

簡単ですがGraphQLのmutationについてまとめてみました。
複数の変更を行いたい場合でも、それぞれの変更に対して独立したミューテーションを定義することで、エンドポイントの使用を単純化し、コードを維持しやすくなるのでそこを意識していきたいです。

Discussion