1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rails × GraphQL でAPI開発|導入からCRUDまでの実践ガイド

Last updated at Posted at 2025-04-20

概要

本記事は Rails × GraphQLの構成で開発を行う方に向けて執筆しています。

この記事では、Rails アプリケーションにおける GraphQL の導入方法や、DB操作方法を実際のコード例を交えて紹介します。

image.png

記事の中で扱うライブラリのバージョンについては以下の通りです。

  • 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クライアントです。

image.png

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")

GraphiQLhttp://localhost:3000/graphiql) を使って、以下クエリを実行します。

{
  user(id: 1) {
    name
    email
  }
}

クエリを実行すると、指定したフィールド(name, email)のみがレスポンスとして返されます。

GraphQLではこのように 必要な項目を明示的に指定する ことで、不要なデータを取得してしまう「オーバーフェッチング」を防ぐことができます👌

image.png

[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")

GraphiQLhttp://localhost:3000/graphiql) で以下のクエリを実行します。
このクエリは、複数のユーザーの nameemail を取得します。

{
  users {
    name
    email
  }
}

クエリを実行すると、レスポンスとして、指定された nameemail の情報を持つ複数のユーザーが返されます。

このようにすることで簡単に複数のレコードを取得することができますね👌

image.png

[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)を渡すことができます。
これにより、動的な値をクエリに渡すことができ、同じクエリ構造で異なるデータを操作することが可能になります。

変数を使う基本的な構文は次のとおりです。

  1. クエリやミューテーションの冒頭で変数を宣言
    mutation CreateUser($user: UserInputType!)
  2. クエリ内で変数を参照
    createUser(user: $user)
  3. 変数を渡してクエリを実行する
    GraphiQLでは、VariablesペインでJSON形式の値を設定

動作確認

それでは動作確認を行います。

Query

mutation createUser($user: UserInputType!) {
  createUser(user: $user) {
    id
  }
}

Variables

{
  "user": {
    "name": "honda",
    "email": "honda@gmail.com",
    "password": "password"
  }
}

クエリを実行すると、作成されたレコードの id の情報が返されます。

image.png

コンソール上でも実際にデータが作成されていることを確認できますね👌

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. UserInputTypeIDフィールドを追加

まず、更新対象のユーザーを識別するため UserInputTypeID 引数を追加します。

IDがあれば更新、 なければ作成としたいので、requiredfalse とします。

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"
  }
}

クエリを実行すると、更新されたレコードの情報が返されます。

image.png

コンソール上でもデータが更新されていることを確認できました🤟

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 が返されているので、削除が成功していそうですね!

image.png

コンソール上でもデータが削除されていることを確認できました✌️

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操作について解説しました。

この他にもバリデーションや認可など、考慮すべき項目がいくつかあるので、それらについては別途まとめていく予定です😎

追記:

ログイン認証方法に関する記事を公開しました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?