こんにちは。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.ymlにttyとstdin_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.rbにrequire "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")