概要
本記事は Rails
× GraphQL
の構成で開発を行う方に向けて執筆しています。
この記事では、Rails
アプリケーションにおける GraphQL
の導入方法や、DB操作方法を実際のコード例を交えて紹介します。
記事の中で扱うライブラリのバージョンについては以下の通りです。
- Rails
7.1.5.1
- GraphQL
2.5.3
Graph QLとは?
GraphQL
は、Facebookが2015年に開発・オープンソース化したAPI向けのクエリ言語およびランタイムです。
従来の REST API
とは異なる設計思想を持ち、クライアントが「必要なデータのみ」を取得できる柔軟性が特徴です。
- 1つのエンドポイントで様々なデータ取得が可能
- オーバーフェッチングの防止
- 型指定が明確
REST API vs GraphQL
GraphQLとREST APIの主な違いを簡単に比較してみます。
特徴 | REST API | GraphQL |
---|---|---|
エンドポイント | 複数エンドポイント | 単一エンドポイント |
取得データ | 固定の構造 | クライアントが必要なデータを指定 |
バージョニング | 明示的なバージョン管理 | スキーマ拡張による暗黙的管理 |
習得難易度 | 比較的容易 | 高め |
エラーハンドリング | HTTPステータスコード | すべて200で返される (レスポンス内でエラー情報提供) |
セットアップ
GraphQL
の概要を理解したところで、早速開発環境を構築していきましょう。
既存のRails
プロジェクトに Graph QL
を導入していきます。
1. Gemの追加、bundle install
以下をGemfile
に追加します。
gem 'graphql'
gem "graphiql-rails", group: :development
追加が完了したら、bundle install
を実行します。
bundle install
2. ジェネレーターを実行
次にGraphQL
で利用する設定ファイルを作成します。
ジェネレーターが用意されているので、コマンドを実行します。
rails g graphql:install
ジェネレーターを実行すると、Graph QLで必要となるファイルが複数作成されます。
eate app/graphql/types
create app/graphql/types/.keep
create app/graphql/myapp_schema.rb
create app/graphql/types/base_object.rb
create app/graphql/types/base_argument.rb
create app/graphql/types/base_field.rb
create app/graphql/types/base_enum.rb
create app/graphql/types/base_input_object.rb
create app/graphql/types/base_interface.rb
create app/graphql/types/base_scalar.rb
create app/graphql/types/base_union.rb
create app/graphql/resolvers/base_resolver.rb
create app/graphql/types/query_type.rb
add_root_type query
create app/graphql/mutations
create app/graphql/mutations/.keep
create app/graphql/mutations/base_mutation.rb
create app/graphql/types/mutation_type.rb
add_root_type mutation
create app/controllers/graphql_controller.rb
route post "/graphql", to: "graphql#execute"
route graphiql-rails
create app/graphql/types/node_type.rb
insert app/graphql/types/query_type.rb
create app/graphql/types/base_connection.rb
create app/graphql/types/base_edge.rb
insert app/graphql/types/base_object.rb
insert app/graphql/types/base_object.rb
insert app/graphql/types/base_union.rb
insert app/graphql/types/base_union.rb
insert app/graphql/types/base_interface.rb
insert app/graphql/types/base_interface.rb
insert app/graphql/myapp_schema.rb
insert config/application.rb
以上でセットアップは完了です。
お疲れ様でした👌
GraphiQL
本セクションではGraphiQL
について解説します。
GraphiQLとは?
GraphQL
を検証するためのGUIクライアントです。
Rails環境では、graphiql-rails
をインストールすることで、http://localhost:3000/graphiql にアクセスすることで利用することができます。
※ホスト名は任意の値に置き換えてください。
本セクションでもGraphiQL
を使用してGraphQL
の検証を行なっていきます。
ActiveRecordからデータを取得する
GraphQL
を用いて、ActiveRecord
のデータを取得する方法について解説します。
ここでは User モデルを例に、単体のデータ取得・一覧取得・カスタムフィールドの扱いなど、基本的なクエリの実装パターンを紹介します。
使用するUser
テーブルのスキーマは以下の通りです。
MySQL [myapp_development]> desc users;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | bigint | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| email | varchar(255) | NO | UNI | NULL | |
| password | varchar(255) | NO | | NULL | |
| created_at | datetime(6) | NO | | NULL | |
| updated_at | datetime(6) | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
以降の検証では、上記User
モデルを使用するため、ハンズオンで進めたい方はモデルを作成してください。
rails g model User name:string email:string:uniq password:string
[事前準備] Typeファイルの作成
データ取得の事前準備として、GraphQL上で使用する型を定義します。
GraphQLでは、レスポンスとしてどのようなデータを返すのかを、あらかじめ定義しておく必要があります。
Railsでは、以下のジェネレーターを使って型定義を自動生成することができます。
bin/rails g graphql:object User
生成された app/graphql/types/user_type.rb
をみていきましょう。
スキーマ情報から型定義が自動作成されていますね🙋♂️
module Types
class UserType < Types::BaseObject
description "User Model"
field :id, ID, null: false
field :name, String, null: false
field :email, String, null: false
field :password, String, null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
end
end
解説
詳細を見ていきます。
🔍 field
とは?
GraphQLでレスポンスとして返す項目(カラムやメソッド)を定義するものです。
上記の例で言うと、id
, name
, email
などがすべて「フィールド」に該当します。
構文は以下のようになります:
field :field_name, type, null: true|false
たとえば、以下の場合は「nameというフィールドに、文字列(String)型の値を返す。nullは許容しない」という意味になります。
field :name, String, null: false
実際のデータがnil
だった場合、GraphQLはエラーとしてレスポンスを返します。
{
"errors": [
{
"message": "Cannot return null for non-nullable field User.email",
//...
}
]
}
🧱 Scalar型とは?
GraphQLでは各フィールドにScalar型(String
, Int
, Boolean
...)を設定します。
これは、数字や文字列など、基本的なデータ型を表すものです。
GraphQL型 | 説明 |
---|---|
ID |
識別子 |
String |
文字列 |
Int |
整数 |
Float |
浮動小数点数 |
Boolean |
true / false |
GraphQL::Types::ISO8601DateTime |
ISO 8601形式の日付・時刻 |
Rubyの graphql-ruby
ライブラリでは、日付やカスタム型なども扱えます。詳細は以下の公式ドキュメントをご覧ください:
[Read] 単一データの取得
事前準備が完了したところで、早速データを取得していきましょう。
本セクションでは特定のユーザーデータ1件を取得する処理を実装していきます。
1. エンドポイントの定義
最初に、User
データを取得するためのエンドポイントを定義します。
この処理は、query_type.rb
に記述します。
query_type.rb
は、GraphQLスキーマにおけるクエリのエントリーポイントとして機能し、クライアントからのデータ取得リクエストを受け付ける役割を担います。
module Types
class QueryType < Types::BaseObject
# ...
field :user, Types::UserType, null: true, description: "ユーザー" do
argument :id, ID, required: true
end
def user(id:)
User.find(id)
end
end
end
解説
🔍 field
- クライアントが呼び出すクエリ名(ここでは
user
)を定義します - このクエリが返すデータの型(
Types::UserType
)と、null
を許容するかどうかを指定します -
description
にはAPIドキュメントとして表示される説明文を設定できます
🛠️ argument
- クエリに渡す 引数(パラメータ) を定義します
-
id: ID, required: true
は、「IDという名前の引数を必須で受け取る」という意味です
🔑 def user(id:)
- クエリが実行された際に呼び出されるリゾルバ関数です
- 実際にデータベースから
User
レコードを取得する処理をここに記述します
動作確認
動作検証のために、テスト用のレコードを1件作成しておきましょう。
User.create!(id: 1, email: "test@example.com", name: "yamada", password: "password")
GraphiQL
(http://localhost:3000/graphiql) を使って、以下クエリを実行します。
{
user(id: 1) {
name
email
}
}
クエリを実行すると、指定したフィールド(name
, email
)のみがレスポンスとして返されます。
GraphQL
ではこのように 必要な項目を明示的に指定する ことで、不要なデータを取得してしまう「オーバーフェッチング」を防ぐことができます👌
[Read] 複数データの取得
次に複数のUserレコードを取得していきましょう。
GraphQLでは、単一のデータだけでなく、複数のデータも簡単に取得できます。
早速進めていきましょう。
1. エンドポイントの定義
query_type.rb
に新しいフィールドを追加します。
module Types
class QueryType < Types::BaseObject
# ...
# --- 追加 ---
field :users, [Types::UserType], null: true, description: "ユーザー一覧"
# --- ここまで ---
def users
User.all
end
end
end
解説
🧳 [Types::UserType]
- GraphQLでは、配列を定義する場合、
[Type]
という形式を使います。 - 今回のケースでは、複数のユーザー情報を取得したいので
[Types::UserType]
としています。
動作確認
動作検証のために、テスト用のレコードを複数件作成しておきましょう。
User.create!(id: 2, email: "test2@example.com", name: "sato", password: "password")
User.create!(id: 3, email: "test3@example.com", name: "suzuki", password: "password")
GraphiQL
(http://localhost:3000/graphiql) で以下のクエリを実行します。
このクエリは、複数のユーザーの name
と email
を取得します。
{
users {
name
email
}
}
クエリを実行すると、レスポンスとして、指定された name
と email
の情報を持つ複数のユーザーが返されます。
このようにすることで簡単に複数のレコードを取得することができますね👌
[Create] Userデータの作成
次にユーザーの作成処理を実装します。
GraphQLでは、データを作成、更新、削除する操作を「Mutation
(ミューテーション)」と呼びます。
データ作成処理の Mutation
を実装していきましょう。
1. UserType の編集
まずはクライアントから受け付けるデータのスキーマを定義します。
types/user_input_type.rb
を新しく作成します。
module Types
class UserInputType < Types::BaseInputObject
graphql_name "UserInputType"
description "User input for creating a new user"
argument :name, String, required: true
argument :email, String, required: true
argument :password, String, required: true
end
end
2. Mutation の作成
次に、app/graphql/mutations/create_user.rb
を作成します。
class Mutations::CreateUser < GraphQL::Schema::Mutation
null true
argument :user, Types::UserInputType, required: true
def resolve(user:)
User.create(user.to_h)
end
end
解説
🧸 argument
-
Mutation
に渡す引数を定義
📂 resolve
- データを操作する処理を記述
3. エンドポイントの定義
最後にエンドポイントを定義します。
mutation_type.rb
は、ミューテーションのエントリーポイント として機能します。
クライアントからのデータ操作リクエスト(作成・更新・削除)を受け付ける役割を担います。
mutation:
には、前段で作成したミューテーション(Mutations::CreateUser
) を指定します。
module Types
class MutationType < Types::BaseObject
# --- 追加 ---
field :create_user, Types::UserType, mutation: Mutations::CreateUser
# --- ここまで ---
end
end
Variables
動作確認の前に、クエリで変数を使う方法を見ていきましょう。
GraphQL
では、クエリに変数(Variables
)を渡すことができます。
これにより、動的な値をクエリに渡すことができ、同じクエリ構造で異なるデータを操作することが可能になります。
変数を使う基本的な構文は次のとおりです。
- クエリやミューテーションの冒頭で変数を宣言
mutation CreateUser($user: UserInputType!)
- クエリ内で変数を参照
createUser(user: $user)
- 変数を渡してクエリを実行する
GraphiQL
では、Variables
ペインでJSON
形式の値を設定
動作確認
それでは動作確認を行います。
Query
mutation createUser($user: UserInputType!) {
createUser(user: $user) {
id
}
}
Variables
{
"user": {
"name": "honda",
"email": "honda@gmail.com",
"password": "password"
}
}
クエリを実行すると、作成されたレコードの id
の情報が返されます。
コンソール上でも実際にデータが作成されていることを確認できますね👌
irb(main):002> User.find(7)
User Load (5.7ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 7 LIMIT 1 /*application='Myapp'*/
=>
#<User:0x0000ffffb8c4bba8
id: 7,
name: "honda",
email: "honda@gmail.com",
password: "[FILTERED]",
created_at: Sat, 19 Apr 2025 01:33:48.016869000 UTC +00:00,
updated_at: Sat, 19 Apr 2025 01:33:48.016869000 UTC +00:00>
[Update] Userデータの更新
ユーザーの作成が完了したところで、ユーザーデータを更新する機能を実装していきます。
1. UserInputType
にID
フィールドを追加
まず、更新対象のユーザーを識別するため UserInputType
に ID
引数を追加します。
ID
があれば更新、 なければ作成としたいので、required
は false
とします。
module Types
class UserInputType < Types::BaseInputObject
graphql_name "UserInputType"
description "User input for creating a new user"
# --- 追加 ---
argument :id, ID, required: false
# --- ここまで ---
argument :name, String, required: true
argument :email, String, required: true
argument :password, String, required: true
end
end
2. Mutation の作成
次に、 app/graphql/mutations/update_user.rb
を作成します。
ここではID
で指定されたユーザーを検索し、渡された情報でユーザーを更新します。
class Mutations::UpdateUser < GraphQL::Schema::Mutation
argument :user, Types::UserInputType, required: true
def resolve(user:)
target_user = User.find(user.id)
target_user.update!(user.to_h)
target_user
end
end
3. エンドポイントの定義
mutation_type.rb
に新しいフィールドを追加して、作成したミューテーションを登録します。
module Types
class MutationType < Types::BaseObject
field :create_user, Types::UserType, mutation: Mutations::CreateUser
# --- 追加 ---
field :update_user, Types::UserType, mutation: Mutations::UpdateUser
# --- ここまで ---
end
end
動作確認
Query
mutation updateUser($user: UserInputType!) {
updateUser(user: $user) {
id
name
email
password
}
}
Variables
{
"user": {
"id": 1,
"name": "name_updated",
"email": "updated@gmail.com",
"password": "password_updated"
}
}
クエリを実行すると、更新されたレコードの情報が返されます。
コンソール上でもデータが更新されていることを確認できました🤟
irb(main):001> User.first
User Load (0.5ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1 /*application:Myapp*/
=>
#<User:0x0000ffff7f395dd0
id: 1,
name: "name_updated",
email: "updated@gmail.com",
password: "[FILTERED]",
created_at: Sun, 20 Apr 2025 10:35:07.309258000 UTC +00:00,
updated_at: Sun, 20 Apr 2025 11:08:15.543580000 UTC +00:00>
[Destroy] Userデータの削除
最後に、ユーザーデータの削除処理を実装します。
削除操作も他の更新操作と同様に、Mutation
として実装します。
1. Mutation の作成
次に、 app/graphql/mutations/destroy_user.rb
を作成します。
ここではIDで指定されたユーザーを検索し、ユーザーを削除します。
class Mutations::DestroyUser < GraphQL::Schema::Mutation
argument :id, ID, required: true
def resolve(id:)
target_user = User.find(id)
target_user.destroy
target_user.destroyed?
end
end
2. エンドポイントの定義
mutation_type.rb
に新しいフィールドを追加して、作成したミューテーションを登録します。
module Types
class MutationType < Types::BaseObject
field :create_user, Types::UserType, mutation: Mutations::CreateUser
field :update_user, Types::UserType, mutation: Mutations::UpdateUser
# --- 追加 ---
field :destroy_user, GraphQL::Types::Boolean, mutation: Mutations::DestroyUser
# --- ここまで ---
end
end
動作確認
動作確認をしていきます。
Query
mutation DestroyUser{
destroyUser(id: "1")
}
クエリを実行します。
"destroyUser": true
が返されているので、削除が成功していそうですね!
コンソール上でもデータが削除されていることを確認できました✌️
irb(main):002> User.find_by(id: 1)
User Load (4.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1 /*application:Myapp*/
=> nil
まとめ
今回はUserモデルを用いた基本的なCRUD操作について解説しました。
この他にもバリデーションや認可など、考慮すべき項目がいくつかあるので、それらについては別途まとめていく予定です😎
追記:
ログイン認証方法に関する記事を公開しました。