RailsでGraphQLサーバーを作成する手順【Docker環境】

こんにちは。Enjoy IT Life管理人の@nishina555です。

この記事では、Docker環境でRails(APIモード)を使ったGraphQLサーバーを構築する手順を紹介します。

今回使用する環境は以下の通りです。

  • Ruby 3.3.2
  • Rails 7.1.3.3
  • MySQL 8.0.21

Railsアプリケーションの準備

今回はrails_graphqlというディレクトリにrails_graphqlという名前のアプリケーションを作成します。

まず、ディレクトリの作成・移動をします。

mkdir rails_graphql && cd $_

Rubyイメージを利用してローカル環境にGemfileを作成します。

docker run --rm -v `pwd`:/rails_graphql -w /rails_graphql ruby:3.3.2 bundle init

作成されたGemfileのgem "rails"をアンコメントし、Railsのバージョンを指定します。

Gemfile

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

- # gem "rails"
+ gem "rails", '~> 7.1.3.3'

rails newを実行するDocker環境を構築するためDockerfileを作成します。APIモードでRailsアプリケーションを作成する場合、Node.jsやYarnの事前インストールは不要です。

Dockerfile.dev

FROM ruby:3.3.2

# 作業ディレクトリを/rails_graphqlに指定
WORKDIR /rails_graphql

# ローカルのGemfileをDockerにコピー
COPY Gemfile* /rails_graphql/

# /rails_graphqlディレクトリ上でbundle install
RUN bundle install

Dockerfileをビルドし、作成したイメージを利用してコンテナ上でrails newを実行します。--apiオプションを追加することでAPIモードのRailsアプリケーションを作成できます。

docker build -t rails_graphql -f Dockerfile.dev .

docker run --rm -v `pwd`:/rails_graphql rails_graphql rails new . --api --skip-bundle --database=mysql

Rails 7.1以降はrails newのタイミングでDockerファイルが自動生成されるようになっています1。自動生成されるDockerファイルは本番用のため、ファイル名をリネームしておきます。

mv Dockerfile Dockerfile.prod

次にdocker-compose.ymlを作成します。データベースの接続ユーザー名はwebuser、パスワードはwebpassとし、データベースの情報はmysql_dataという名前付きボリュームで永続化します。

docker-compose.yml

version: '3'
services:
  api: # Ruby on Railsが起動するコンテナ
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - '3000:3000'
    volumes:
      - .:/rails_graphql
      - gem_data:/usr/local/bundle
    depends_on:
      - db
    command: ["rails", "server", "-b", "0.0.0.0"]
  db: # MySQLが起動するコンテナ
    platform: linux/x86_64
    image: mysql:8.0.21
    volumes:
      - mysql_data:/var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_USER: 'webuser'
      MYSQL_PASSWORD: 'webpass'
      MYSQL_ROOT_PASSWORD: 'pass'
      MYSQL_DATABASE: 'rails_graphql_development'
volumes:
  mysql_data:
  gem_data:

Ruby on Railsのデータベース接続設定を変更します。

config/database.yml

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: webuser
  password: webpass
  host: db

Railsアプリケーションの起動確認

docker-compose upを実行後、localhost:3000にアクセスしてRailsの初期画面が表示されればOKです。

テスト環境用のデータベースの作成

development環境のデータベースは正常に作成されましたが、test環境のデータベースはまだ用意されていません。MySQLのDockerイメージでは/docker-entrypoint-initdb.dにスクリプト(.sql.sh.sql.gz)を配置しておくと、コンテナ起動時に自動実行してくれる機能があります2

mkdir docker-entrypoint-initdb.d && cd $_

00_create.sql

-- test環境用のデータベースを作成する
CREATE DATABASE rails_graphql_test;

01_grant.sql

-- webuserという名前の一般ユーザーを利用する場合
GRANT ALL ON `rails_graphql_test`.* TO webuser@'%';

作成したスクリプトをコンテナ上で読み取れるようにするため、docker-compose.ymlのdbサービスにバインドマウントを追加します。

  db:
    platform: linux/x86_64
    image: mysql:8.0.21
    volumes:
      - mysql_data:/var/lib/mysql
+     - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
    command: --default-authentication-plugin=mysql_native_password

git clone → docker-compose upで起動できるようにする

コンテナ起動時にマイグレーションを自動実行させることで、「git clone → docker-compose up」だけで環境が用意できるようにします。

マイグレーションを実行するスクリプトを作成します。

start.sh

#!/bin/bash -eu
rails db:migrate
rm -rf tmp/pids/server.pid
rails server -b '0.0.0.0'

スクリプトのパーミッションを変更します。

chmod 755 start.sh

Rails側で行うdb:migrateコマンドはMySQLコンテナの準備が完了してから実行する必要があります。今回はwait-for-it.shを利用してデータベースコンテナの準備完了を待つようにします。

wait-for-it.shをプロジェクトのルートディレクトリにコピーし、docker-compose.ymlを以下のように修正します。

  api:
    ...
    depends_on:
      - db
-   command: ["rails", "server", "-b", "0.0.0.0"]
+   command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
chmod 755 wait-for-it.sh

デバッグ環境の準備

docker-compose.ymlttystdin_openを追加し、Gemfileにpry-byebugを追加することで、binding.pryによるデバッグが可能になります。

services:
  api:
    ...
+   tty: true
+   stdin_open: true
# Gemfile
group :development, :test do
+ gem 'pry-byebug'
end

GraphQLサーバーの作成

graphql gemをインストールし、GraphQLの初期設定を行います。

docker compose exec api bundle add graphql
docker compose exec api rails generate graphql:install

GraphiQLの準備

開発環境でGraphQLのクエリを試すために、GraphiQLを導入します。

Gemfileにgraphiql-railsを追加します。

group :development do
+  gem "graphiql-rails"
end
docker compose exec api bundle install

ルーティングにGraphiQLのマウントを追加します。

config/routes.rb

if Rails.env.development?
  mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: '/graphql'
end

APIモードの場合は、アセットパイプライン用のgemも必要です3

docker compose exec api bundle add sprockets-rails
# または
docker compose exec api bundle add propshaft

sprockets-railsを利用する場合は、application.rbrequire "sprockets/railtie"を追加します(propshaftの場合はこの変更は不要です)。

application.rb

require "sprockets/railtie"

app/assets/config/manifest.js

//= link graphiql/rails/application.css
//= link graphiql/rails/application.js

GraphiQLの動作確認

docker compose upした後、http://localhost:3000/graphiqlにアクセスし、以下のクエリを実行してレスポンスが返ることを確認します。

query {
  testField
}

Queryの作成

実際にモデルを作成してGraphQLのQueryを実装していきます。

まず、Eventモデルを作成してデータを用意します。

docker-compose exec api rails g scaffold event title:string
docker-compose exec api rails db:migrate
docker-compose exec api rails c
> event = Event.new(title: 'sample event')
> event.save

一覧取得(index)

GraphQLの型定義を作成します。

app/graphql/types/event_type.rb

module Types
  class EventType < Types::BaseObject
    field :id, ID, null: false
    field :title, String, null: true
  end
end

QueryTypeにeventsフィールドを追加します。

app/graphql/types/query_type.rb

module Types
  class QueryType < Types::BaseObject
    field :events, [Types::EventType], null: false

    def events
      Event.all
    end
  end
end

GraphiQLで以下のクエリを実行すると、イベントの一覧が取得できます。

query {
  events {
    id
    title
  }
}

詳細取得(show)

QueryTypeにeventフィールドを追加し、IDで検索できるようにします。

app/graphql/types/query_type.rb

module Types
  class QueryType < Types::BaseObject
    field :event, Types::EventType, null: false do
      argument :id, ID, required: true
    end

    def event(id:)
      Event.find_by(id: id)
    end
  end
end

GraphiQLで以下のクエリを実行すると、指定したIDのイベントが取得できます。

query {
  event(id: "1") {
    id
    title
  }
}

スキーマのdumpをできるようにする

GraphQLスキーマをファイルに出力できるようにRakeタスクを追加します。

lib/tasks/graphql.rake

require "graphql/rake_task"
GraphQL::RakeTask.new(schema_name: "RailsGraphqlSchema")

参考

Footnotes

  1. Docker + Rails7.1 の環境構築で詰まったポイント

  2. Docker Hub『MySQL: Initializing a fresh instance』

  3. rmosolgo/graphiql-rails

タグ: Rails , GraphQL , Docker