第2章: Rails APIプロジェクト雛形
🎯 この章で学ぶこと(所要時間: 3時間)
この章では、Rails APIモードでプロジェクトを構築し、必要なGemの導入とテスト環境をセットアップします。また、実際の開発で必須となるGitブランチの使い方も学びます。
📢 はじめに
第1章ではDockerを使ってRailsが動く環境を構築しました。
第2章では本格的なAPIサーバーの土台を作っていきます。現代のWeb開発では、フロントエンドとバックエンドを分離することが一般的です。InstagramやTwitter、メルカリなど、みなさんが普段使っているアプリも、裏側ではAPIサーバーが動いています。
この章では、そんなモダンなWeb開発に必要な「Rails APIモード」を使って、プロジェクトの基盤を整えていきます。また、チーム開発で必須の「Gitブランチ」の使い方も実践的に学びます。
🎓 この章を終えると...
- なぜAPIモードを使うのか、その理由が腹落ちする
- Gitブランチを使った安全な開発フローが身につく
- プロ仕様のGem選定ができるようになる
- テスト駆動開発(TDD)の第一歩を踏み出せる
- 実際の開発現場で使われている設定が理解できる
💡 この章で身につくスキル
- 🌳 Gitブランチを使った機能開発フロー
- 🚀 Rails APIモードの理解と構築
- 💎 必要なGemの選定と設定
- 🗄️ データベース接続の仕組み
- 🌐 CORS(クロスオリジンリソース共有)の基礎
- 🧪 RSpecによるテスト環境構築
- 🔍 ブラウザでのAPI動作確認
- まずGitブランチについて学び、作業用ブランチを作成します
- Rails APIモードの概念を理解します
- 実際に手を動かしながら、順番に設定していきます
- 最後にGitHubにプッシュして、プルリクエストを作成します
- 第1章でDockerコンテナが正常に動作していること
- GitHubアカウントがあり、リポジトリをクローンしていること
- ターミナルでコマンドが実行できること
それでは、プロの開発現場で使われている技術を、一つずつ身につけていきます。
📚 0. Gitブランチ入門
🌳 ブランチとは
開発を始める前に、Gitブランチについて理解します。ブランチは、メインの開発ラインから分岐して、独立して作業できる機能です。
想像してみてください。あなたが本を書いているとします:
- mainブランチ = 出版される完成原稿
- 作業ブランチ = 下書きや修正用のコピー
下書きで失敗しても、完成原稿には影響しません。
🔨 ブランチの作成
ここまでは概念の説明でした。実際に手を動かしていきます。
実際にGitブランチを作成して、第2章の作業を始めます。以下のコマンドを順番に実行していきます。
まず現在の状態を確認します:
# 現在のブランチを確認
git branch
# => * main と表示される
# 現在の変更状況を確認
git status
# => nothing to commit と表示される
作業用ブランチを作成して切り替えます:
# feature/chapter-2 というブランチを作成して切り替え
git checkout -b feature/chapter-2
# ブランチが切り替わったか確認
git branch
# => * feature/chapter-2 と表示される
feature/機能名: 新機能開発fix/バグ名: バグ修正refactor/対象: リファクタリング
これで準備完了です。以降の作業は全てこのブランチで行います。
1. Rails APIモードとは?
🤔 モダンなWebアプリケーション開発へ
Rails Tutorialでは、HTMLページを生成して返すWebアプリケーションを作りました。ユーザー登録、ログイン、マイクロポストの投稿など、全ての機能がHTMLとして返されていました。
最近のWebサービスでは、TwitterやInstagramのように、ブラウザだけでなくスマホアプリからも同じ投稿にアクセスできます。これはAPIサーバーという仕組みを使っているからです。
今回は、Rails Tutorialで学んだ知識を活かして、こうしたモダンなアプリケーションの作り方を学んでいきます。Ruby on Railsには、APIサーバーを効率的に作るためのRails APIモードという機能があるので、それを使っていきます。
📱 APIとは
**API(Application Programming Interface)**は、アプリケーション同士がデータをやり取りするための「窓口」です。
簡単に言うと:
- フロントエンド(React、iOSアプリ、Androidアプリなど)が「データをください」とリクエスト
- Rails APIが必要なデータをJSON形式で返す
- フロントエンドがそのデータを使って画面を作る
📄 JSONとは
APIがデータを返すときによく使われる**JSON(JavaScript Object Notation)**について説明します。
JSON = 誰でも読めるデータ形式
Rails Tutorialでは:
- ユーザー情報をHTMLで表示していました
<h1>Michael Hartl</h1><p>michael@example.com</p>のような形
APIサーバーでは、同じ情報をJSON形式で返します:
{
"user": {
"name": "Michael Hartl",
"email": "michael@example.com",
"created_at": "2024-01-05T10:00:00Z"
}
}
なぜJSONを使うの?
- 📱 iPhoneアプリも読める
- 💻 Reactアプリも読める
- 🤖 Androidアプリも読める
- 全員が同じデータを受け取って、それぞれの方法で表示する!
🔍 実際のAPIを体験してみよう!
ここで実際のAPIがどう動くか体験してみましょう。以下は郵便番号検索APIを呼び出す例です:
🏮 郵便番号検索APIデモ
実際のAPIを呼び出して、JSONデータがどのように返されるか見てみましょう!
上のデモでは:
- **あなた(ブラウザ)**が郵便番号を入力
- 郵便番号検索APIにリクエストを送信
- APIがJSON形式で住所データを返す
- Reactがそのデータを受け取って画面に表示
これがまさにAPIの仕組みです!Rails APIモードで作るサーバーも、同じようにJSONデータを返します。
🤔 Rails TutorialとAPIモードの違い
Rails Tutorial方式(通常のRails):
Rails Tutorialのレスポンス例(HTML):
<html>
<body>
<h1>All users</h1>
<ul class="users">
<li>
<img src="/assets/gravatar.jpg" alt="Michael Hartl">
<a href="/users/1">Michael Hartl</a>
</li>
</ul>
</body>
</html>
Rails APIモードの動き
レスポンス例(JSON):
{
"todos": [
{
"id": 1,
"title": "買い物に行く",
"completed": false
},
{
"id": 2,
"title": "宿題をする",
"completed": true
}
]
}
🌍 1つのAPIを複数のアプリが使う仕組み
従来の方法(それぞれ別々に作る):
❌ 同じデータなのに3つも管理が必要...
APIモードを使った方法:
✅ 1つのデータベースを共有!
- データの一元管理 → 更新が1箇所で済む
- 開発効率UP → APIを1つ作れば全アプリで使える
- 一貫性の保証 → 全員が同じデータを見る
🌍 実際のサービスでの利用例
Rails APIモードが実際にどう使われているか見てみましょう:
Instagramは1つのAPIで複数のプラットフォームに対応:
1つのRails API(実際にはPythonですが、仕組みは同じ)が、様々なクライアントにデータを提供:
- 📱 iOSアプリ → Swift/SwiftUIで作られたネイティブアプリ
- 🤖 Androidアプリ → Kotlin/Jetpack Composeで作られたネイティブアプリ
- 💻 instagram.com → React.jsで作られたWebアプリ
- ⌚ Apple Watch → 通知機能だけの軽量版
- 🖥️ デスクトップアプリ → Electronで作られたアプリ
全部違う見た目・操作感だけど、データの出どころは同じAPI!
他にも:
- Twitter(X): 1つのAPIで Web/iOS/Android/TweetDeck に対応
- メルカリ: 1つのAPIで Web/iOS/Android/メルペイ に対応
- Netflix: 1つのAPIで Web/iOS/Android/TV/ゲーム機 に対応
-
開発チームを分けられる
- バックエンドチーム:Ruby/Railsのエキスパート
- フロントエンドチーム:React/Vue/Swiftのエキスパート
-
それぞれ得意な技術を使える
- iOS開発者:Swift で最高のiPhoneアプリを作る
- Web開発者:React で美しいWebサイトを作る
- みんな同じAPIからデータを取得
-
メンテナンスが楽
- APIを1箇所修正すれば、全てのアプリに反映
- 新機能の追加も1箇所でOK
📊 通常のRailsとAPIモードの違い
| 機能 | 通常のRails | Rails APIモード | なぜ違う? |
|---|---|---|---|
| レスポンス形式 | HTML(Webページ) | JSON(データのみ) | フロントエンドが画面を作るから |
| セッション管理 | Cookieでユーザー情報保持 | トークンベース | 複数デバイス対応のため |
| CSRF対策 | 自動で有効 | 不要(トークンで認証) | APIは別の認証方式を使うから |
| ミドルウェア | 多い(画面表示用) | 最小限 | 速度重視! |
- 🚀 軽量・高速: 不要な機能を省いてパフォーマンス向上
- 📱 マルチデバイス対応: Web、モバイル、デスクトップアプリから利用可能
- 🔄 フロントエンド独立: React、Vue、Flutterなど何でも使える
- 📈 スケーラブル: サーバーを増やしやすい設計
2. プロジェクトの構成確認
📁 現在のディレクトリ構成
前章で作成したDockerコンテナとRailsプロジェクトを使用します。まず環境が正しく動作しているか確認しましょう。
前章で作成した環境が正しく動作しているか確認します。以下のコマンドを実行してください。
🔨 実際にやってみましょう! 環境の確認から始めます:
# プロジェクトディレクトリに移動
cd todo-api
# Dockerコンテナが起動していることを確認
docker compose ps
期待される出力:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
todo-api-db-1 mysql:8.0 "docker-entrypoint.s…" db 2 hours ago Up 2 hours 3306/tcp, 33060/tcp
todo-api-web-1 todo-api-web "entrypoint.sh bash …" web 2 hours ago Up 2 hours 0.0.0.0:3000->3000/tcp
STATUSカラムを確認してください:
Up 2 hoursやrunningと表示されていれば、コンテナは正常に起動しています- 「Up」の後の時間(例:2 hours)は起動してからの経過時間を表します
- もし
Exitedや何も表示されない場合は、docker compose up -dで起動します
🛠 プロジェクトの構成を理解しよう
プロジェクトの構成を確認しますが、まずは下記のコマンドを実行して、その後の説明を読んでください。
🔨 実際にやってみましょう! プロジェクトの構成を確認します:
# プロジェクトのファイル構成を確認
ls -la
# Railsのバージョンを確認
docker compose exec web rails --version
生成されたファイル構造の重要な部分:
todo-api/
├── Dockerfile # Dockerイメージの設定
├── docker-compose.yml # Docker Compose設定
├── Gemfile # Rubyの依存関係管理
├── Gemfile.lock # 依存関係のバージョン固定
├── app/
│ ├── controllers/ # APIエンドポイントの処理
│ ├── models/ # データベースとの対話
│ └── ...
├── config/
│ ├── application.rb # アプリ全体の設定
│ ├── database.yml # DB接続設定
│ └── ...
└── ...
app/views/: HTMLテンプレートは不要app/assets/: CSS/JavaScriptも不要app/helpers/: ビューヘルパーも不要
3. 必要なGemの追加
💎 Gemとは
Gemは、Rubyの世界での「パッケージ」や「ライブラリ」のことです。npmパッケージのRuby版だと思ってください。
Gemは「料理の調味料」のようなもの:
- 基本のRails = 塩・砂糖・醤油(基本調味料)
- 追加Gem = オイスターソース、豆板醤(特殊調味料)
必要に応じて追加することで、より高機能なアプリが作れます。
🎯 今回追加するGemたち
ここまでGemの概念について説明しました。実際にGemを追加していきます。
Gemfileを編集して、必要なGemを追加します。以下の内容をそのままコピーして使ってください。
🔨 実際にやってみましょう! Gemfileを以下の内容で編集してください:
source "https://rubygems.org"
# Rubyバージョンの指定(3.4.0以上3.5.0未満を許可)
ruby "~> 3.4.0"
# ===== 基本のGem =====
gem "rails", "~> 8.0.2" # Railsフレームワーク本体
gem "mysql2", "~> 0.5" # MySQLデータベース接続用
gem "puma", ">= 5.0" # Webサーバー(高速・安定)
# ===== 認証・セキュリティ関連 =====
gem "bcrypt", "~> 3.1.7" # パスワードの暗号化(必須!)
gem "rack-cors" # 別ドメインからのアクセス許可
# ===== API開発用 =====
gem "active_model_serializers", "~> 0.10.0" # JSONレスポンスを綺麗に整形
# ===== 共通ユーティリティ =====
gem "tzinfo-data", platforms: %i[ windows jruby ] # タイムゾーン情報
gem "bootsnap", require: false # 起動高速化
# ===== 開発・テスト環境用 =====
group :development, :test do
gem "debug", platforms: %i[ mri windows ] # デバッグツール
gem "dotenv-rails" # 環境変数を.envファイルで管理
gem "rspec-rails", "~> 6.0" # テストフレームワーク(RSpec)
gem "factory_bot_rails" # テストデータ作成を簡単に
gem "faker" # ダミーデータ生成(テスト用)
end
# ===== 開発環境専用 =====
group :development do
gem "better_errors" # エラー画面を見やすく
gem "binding_of_caller" # エラー画面で変数の中身を確認
gem "rubocop", require: false # Rubyコードの書き方チェック
gem "rubocop-rails", require: false # Rails専用ルール
gem "rubocop-rspec", require: false # RSpec専用ルール
end
# ===== テスト環境専用 =====
group :test do
gem "shoulda-matchers", "~> 5.0" # テストを簡潔に書ける
gem "database_cleaner-active_record" # テスト後のDB掃除
gem "simplecov", require: false # テストカバレッジ測定
end
📦 Gemのインストール
🔨 実際にやってみましょう! 追加したGemをインストールします:
# まず、Dockerコンテナ内でbundle installを実行してGemfile.lockを生成
docker compose run --rm web bundle install
# Gemfile.lockが生成されたら、Dockerイメージを再ビルド
docker compose build
# ビルドが完了したら、コンテナを再起動
docker compose up -d
-
docker compose run --rm web bundle install- Dockerコンテナ内でbundle installを実行
- Gemfile.lockが生成/更新される
--rmオプションで実行後にコンテナを自動削除
-
docker compose build- Gemfile.lockを含めてイメージを再ビルド
- 次回からは高速に起動できる
-
docker compose up -d- 新しいイメージでコンテナを起動
この方法により、ホストマシンにRubyがなくても、Docker内で完結できます!
第1章のボリュームマウントの説明を思い出してください:
volumes:
- .:/app # ホストの現在のディレクトリをコンテナの/appにマウント
このボリュームマウントにより:
- ホスト → コンテナ: ホストのファイル変更が即座にコンテナに反映
- コンテナ → ホスト: コンテナ内でのファイル作成・更新もホストに反映
つまり、docker compose run --rm web bundle installでコンテナ内に生成されたGemfile.lockは、自動的にホストマシン側にも作成されます!これがDockerボリュームの便利な点です。
💾 最初のコミット
Gemの追加は重要な変更なので、ここでコミットしておきましょう。
🔨 実際にやってみましょう! GemfileとGemfile.lockの変更をコミットします:
# 変更内容を確認
git status
git diff Gemfile
# GemfileとGemfile.lockの変更をコミット
git add Gemfile Gemfile.lock
git commit -m "Gemfile更新"
- 変更を小さく分けることで、後から履歴を追いやすくなる
- 問題が起きたときに、どの変更が原因か特定しやすい
- チーム開発では、他のメンバーが理解しやすい
4. データベース設定
🗄️ データベース接続の仕組み
第1章で学んだDockerのネットワークについておさらいします。
📋 database.ymlの確認
前章で環境変数の設定は完了していますが、念のためconfig/database.ymlが正しく設定されているか確認しましょう。
以下の内容になっているか確認するだけです。既に設定済みなので、編集は不要です。
📝 config/database.ymlが以下のようになっていることを確認してください:
default: &default
adapter: mysql2 # MySQLを使用
encoding: utf8mb4 # 絵文字も保存できる文字コード
host: <%= ENV['DATABASE_HOST'] %> # 環境変数から読み込み
username: <%= ENV['DATABASE_USER'] %> # 環境変数から読み込み
password: <%= ENV['DATABASE_PASSWORD'] %> # 環境変数から読み込み
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> # 接続プール数
development:
<<: *default # defaultの設定を継承
database: todo_development # 開発用データベース名
test:
<<: *default
database: todo_test # テスト用データベース名
production:
<<: *default
database: todo_production # 本番用データベース名
- セキュリティ: パスワードをコードに直接書かない
- 柔軟性: 環境ごとに異なる設定を簡単に切り替え
- チーム開発: 各自の環境に合わせた設定が可能
5. CORS設定
🌐 CORSとは
**CORS(Cross-Origin Resource Sharing)**は、異なるドメイン間でのデータのやり取りを制御する仕組みです。
CORSは「マンションのオートロック」のようなもの:
- オートロックなし = 誰でも入れる(セキュリティリスク)
- オートロックあり = 許可された人だけ入れる(安全)
RailsのAPIも、許可したフロントエンドからのアクセスだけを受け付けるようにします!
🔧 CORSの設定
CORSの概念を理解したところで、実際に設定していきましょう。
Rails APIがフロントエンドからのアクセスを受け付けられるよう、CORSを設定します。
🔨 実際にやってみましょう! config/initializers/cors.rbを以下の内容で編集してください:
# CORSの設定ファイル
# このファイルを変更したらサーバーの再起動が必要です
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
# 開発環境では指定したURLからのアクセスを許可
# 本番環境では、実際のフロントエンドのURLに変更する
origins ENV.fetch("FRONTEND_URL", "http://localhost:3001")
# 許可する内容の詳細設定
resource "*", # 全てのAPIパスで有効
headers: :any, # 全てのヘッダーを許可
methods: [:get, :post, :put, :patch, :delete, :options, :head], # 許可するHTTPメソッド
expose: ["Authorization"], # フロントエンドに公開するヘッダー
max_age: 600 # プリフライトリクエストのキャッシュ時間(秒)
end
end
本番環境では、originsに*(全てのドメイン)を指定するのは危険です!
必ず特定のドメインのみを許可してください。
💾 CORS設定をコミット
CORS設定が完了したので、コミットしておきましょう。
🔨 実際にやってみましょう! CORS設定をコミットします:
# 変更内容を確認
git status
git diff config/initializers/cors.rb
# CORS設定をコミット
git add config/initializers/cors.rb
git commit -m "CORS設定"
6. RSpecの設定
🧪 テストコードの重要性を再確認
Rails Tutorialでもテストコードを書きましたね。ユーザー登録のテスト、ログインのテスト、マイクロポストの投稿テストなど、機能を実装するたびにテストを書いて、コードが正しく動作することを確認しました。
テストは、あなたが書いたコードが期待通りに動作することを自動的に確認する仕組みです。
例えば、Rails Tutorialで書いたこんなテストを思い出してください:
- ユーザー登録で、同じメールアドレスは登録できない
- ログインしていないユーザーは、投稿ページにアクセスできない
- 投稿を削除すると、本当にデータベースから消える
これらを毎回手動で確認するのは大変ですよね。テストコードがあれば、rails test(今回はrspec)を実行するだけで、全ての機能が正しく動作しているか一瞬で確認できます。
🔄 MinitestからRSpecへ
Rails TutorialではMinitestというテストフレームワークを使いました。今回は業界標準のRSpecを使います。基本的な考え方は同じですが、書き方がより自然で読みやすくなります。
| 特徴 | Minitest | RSpec |
|---|---|---|
| 書き方 | Rubyの標準的な書き方 | 英語に近い自然な書き方 |
| 学習コスト | 低い(シンプル) | やや高い(機能豊富) |
| 採用企業 | 一部 | 多数(業界標準) |
# Minitest(Rails Tutorialで書いた書き方)
test "should create todo" do
assert_difference('Todo.count') do
post todos_url, params: { todo: { title: "Test" } }
end
assert_redirected_to todo_url(Todo.last)
end
# RSpec(今回使う書き方 - より読みやすい!)
it "creates a new todo" do
expect {
post todos_url, params: { todo: { title: "Test" } }
}.to change(Todo, :count).by(1)
expect(response).to have_http_status(:created)
end
どちらも「POSTリクエストを送ったらTodoが1つ増える」ことをテストしていますが、RSpecの方が「期待する(expect)」「変化する(change)」という自然な英語で書けます。
🎯 APIサーバーのテストの特徴
Rails Tutorialでは、HTMLページが正しく表示されるかをテストしました。APIサーバーでは、以下のような点をテストします:
- HTTPステータスコード:200(成功)、401(認証エラー)、404(見つからない)など
- JSONレスポンスの形式:期待通りのデータ構造か
- データの内容:正しい値が返されているか
- 認証・認可:権限のないユーザーがアクセスできないか
これから作るTodoアプリでも、これらの観点でテストを書いていきます。
🔄 テスト駆動開発(TDD)の第一歩
今回のプロジェクトでは、**テスト駆動開発(Test-Driven Development: TDD)**の考え方を取り入れていきます。
- Red(赤): まずテストを書く → 当然失敗する(実装がないから)
- Green(緑): テストが通る最小限のコードを書く
- Refactor(リファクタリング): コードをきれいに整理する
この章では、まさにこのサイクルを体験します:
- ヘルスチェックのテストを書く(Red)
- コントローラーを実装する(Green)
- 必要に応じてコードを整理する(Refactor)
🔧 RSpecの初期設定
それでは、実際にRSpecをセットアップしていきます。
RSpecの初期設定を行います。これによりテスト環境が構築されます。
🔨 実際にやってみましょう! RSpecの初期設定を行います:
# RSpecの初期設定
docker compose exec web rails generate rspec:install
生成されるファイル:
.rspec: RSpecの基本設定spec/spec_helper.rb: RSpec全体の設定spec/rails_helper.rb: Rails固有の設定
📝 rails_helper.rbの設定
生成されたファイルをカスタマイズして、より使いやすくしましょう。ここで設定する内容は、テストを効率的に書くためのヘルパーメソッドやデータベースのクリーンアップなど、テスト全体に関わる重要な設定です。
RSpecの設定ファイルを編集します。以下の内容をそのままコピーして使ってください。
- FactoryBot: テスト用のデータを簡単に作成できるツール
- DatabaseCleaner: 各テスト後にデータベースをきれいにする
- Shoulda Matchers: よく使うテストを1行で書けるようにする
🔨 実際にやってみましょう! spec/rails_helper.rbを以下の内容で編集してください:
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
# 本番環境でテストを実行しないようにガード
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
# spec/support配下のファイルを読み込み
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
# マイグレーションの状態をチェック
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
abort e.to_s.strip
end
RSpec.configure do |config|
# FactoryBotのメソッドを使えるように
config.include FactoryBot::Syntax::Methods
# Database Cleanerの設定(テスト後にDBをクリーン)
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.around(:each) do |example|
DatabaseCleaner.cleaning do
example.run
end
end
# 基本設定
config.fixture_path = Rails.root.join('spec/fixtures')
config.use_transactional_fixtures = true
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
end
# Shoulda Matchersの設定(テストを簡潔に書ける)
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
🎯 動作確認用のテストを作成
設定が完了したので、実際に動作するテストを書いてみましょう。
シンプルなヘルスチェックエンドポイントを作成し、テストを書いていきます。
ヘルスチェックは、APIサーバーが正常に動作しているかを確認するための仕組みです。
- 通常
/healthや/pingといったパスで提供 - 単純に
{"status": "ok"}のようなレスポンスを返す - 監視ツールやロードバランサーが定期的にアクセスして死活監視に使用
- 本番環境では必須の機能です!
🔨 実際にやってみましょう! まず、テスト用のディレクトリを作成します:
# specディレクトリ内にrequestsディレクトリを作成
docker compose exec web mkdir -p spec/requests
🔨 実際にやってみましょう! spec/requests/health_spec.rbを作成します:
require 'rails_helper'
RSpec.describe "Health Check", type: :request do
describe "GET /health" do
it "returns success status" do
get "/health"
expect(response).to have_http_status(:success)
expect(JSON.parse(response.body)).to eq({"status" => "ok"})
end
end
end
🔴 まずテストが失敗することを確認(TDDのRedフェーズ)
TDDの原則に従って、まず実装なしでテストを実行し、失敗することを確認します。
🔨 実際にやってみましょう! まだ実装していない状態でテストを実行します:
# RSpecを実行(まだ実装がないので失敗するはず)
docker compose exec web rspec
期待される出力(エラー):
Health Check
GET /health
returns success status (FAILED - 1)
Failures:
1) Health Check GET /health returns success status
Failure/Error: get "/health"
ActionController::RoutingError:
No route matches [GET] "/health"
このエラーは「/healthへのルートが存在しない」ことを示しています。これで、テストが正しく書けていることが確認できました。もしテストが最初から成功してしまったら、テスト自体が間違っている可能性があります。
🛣️ ルーティングとコントローラーの作成(TDDのGreenフェーズ)
テストが失敗することを確認できたので、それを通すための実装を追加しましょう。
テストを通すために、ルートとコントローラーを実装します。
🔨 実際にやってみましょう! テストを通すために、ルーティングとコントローラーを設定します:
Rails.application.routes.draw do
# ヘルスチェック用のエンドポイント
get '/health', to: 'application#health'
end
class ApplicationController < ActionController::API
# ヘルスチェック用のアクション
def health
render json: { status: 'ok' }
end
end
✅ テストが成功することを確認(TDDのGreenフェーズ完了)
実装が完了したので、先ほど失敗したテストが今度は成功することを確認します。
RSpecを実行して、テストが通ることを確認します。
🔨 実際にやってみましょう! 再度テストを実行してみましょう:
# RSpecを実行(今度は成功するはず)
docker compose exec web rspec
期待される出力(成功):
Health Check
GET /health
returns success status
Finished in 0.12345 seconds (files took 1.23 seconds to load)
1 example, 0 failures
おめでとうございます!これでTDDの1サイクルが完了しました:
- Red: テストを書いて失敗を確認 ✅
- Green: 最小限の実装でテストを通す ✅
- Refactor: 今回はシンプルな実装なのでリファクタリングは不要
**TDD(テスト駆動開発)**は、開発現場でも意見が分かれる手法です:
賛成派の意見:
- テストを先に書くことで、要件が明確になる
- 実装が最小限になり、無駄なコードが減る
- テストカバレッジが自然と高くなる
慎重派の意見:
- 開発速度が遅くなることがある
- 要件が不明確な場合、テストを書きづらい
- リファクタリング時にテストも修正が必要
このプロジェクトでは:
- 重要な機能にはテストを書く(必須)
- TDDを使うかは状況に応じて判断
- まず動くものを作り、後からテストを追加することもある
大切なのは「テストがあること」であり、「いつ書くか」は柔軟に考えましょう。
🔍 ブラウザでの動作確認
テストが通ったら、実際にブラウザでもアクセスしてみましょう!
ブラウザで実際にAPIにアクセスして、JSONレスポンスを確認します。
🔨 実際にやってみましょう! サーバーが起動していることを確認して、ブラウザでアクセスします:
# サーバーの状態を確認
docker compose ps
# 起動していない場合は起動
docker compose up -d
ブラウザで以下のURLにアクセス:
http://localhost:3000/health
期待される画面:
{"status":"ok"}
📸 スクリーンショット:ブラウザでhealth エンドポイントにアクセスした結果
curl http://localhost:3000/health
💾 RSpecとヘルスチェックAPIをコミット
テスト環境とAPIエンドポイントが動作したので、コミットしましょう。
🔨 実際にやってみましょう! RSpec関連の変更をコミットします:
# 変更内容を確認
git status
# RSpec関連とヘルスチェックAPIをコミット
git add .
git commit -m "RSpec追加"
7. 開発用の便利な設定
🛠 Better Errorsの設定
エラーが発生した時に、より詳しい情報を表示してくれる設定を追加します。
開発を効率化するための設定を追加していきます。
🔨 実際にやってみましょう! config/environments/development.rbに以下を追加してください:
Rails.application.configure do
# ... 既存の設定 ...
# Better Errorsの設定(開発環境のみ)
# Dockerコンテナ内からのアクセスを許可
BetterErrors::Middleware.allow_ip! "0.0.0.0/0" if defined?(BetterErrors)
end
📏 RuboCopの設定 - コード品質を自動でチェック
🤔 なぜコードスタイルの統一が重要?
チーム開発では、メンバー全員が同じルールでコードを書くことが重要です。人によって書き方が違うと:
- コードレビューで本質的でない指摘が増える(インデントや改行の位置など)
- 読みづらいコードになり、バグの温床になる
- Git の差分が見づらくなる
RuboCopは、Rubyコードの書き方を自動でチェックしてくれるツール(Linter)です。
Linterは、コードの問題を自動的に検出してくれるツールです:
- 構文エラー: 文法的に間違っているコード
- スタイル違反: チームで決めたルールに沿っていないコード
- 潜在的なバグ: 未使用の変数、到達不可能なコードなど
- 複雑すぎるコード: メソッドが長すぎる、ネストが深すぎるなど
JavaScript では ESLint、Python では Pylint など、各言語に Linter があります。
🔄 RuboCopができること
-
問題の検出
# 悪い例(RuboCopが指摘)
def calculate_price(items)
price=0 # スペースがない
items.each do |item|
price+=item.price # スペースがない
end
return price # 不要なreturn
end -
自動修正
# RuboCop実行後(自動修正される)
def calculate_price(items)
price = 0
items.each do |item|
price += item.price
end
price
end -
複雑度の警告
- メソッドが長すぎる(10行以上)
- ABC複雑度が高い
- 循環的複雑度が高い
RuboCopの設定ファイルを作成し、実際に実行してみます。
🔨 実際にやってみましょう! .rubocop.ymlを作成します:
# RuboCopの設定ファイル
# チーム全体でコーディングスタイルを統一するため
require:
- rubocop-rails # Rails用のルール
- rubocop-rspec # RSpec用のルール
AllCops:
NewCops: enable # 新しいルールを自動で有効化
Exclude: # チェック対象外のファイル
- 'db/**/*' # マイグレーションファイル
- 'bin/**/*' # 実行ファイル
- 'config/**/*' # 設定ファイル
- 'vendor/**/*' # 外部ライブラリ
- 'spec/spec_helper.rb'
- 'spec/rails_helper.rb'
# クラスやモジュールのコメントは不要
Style/Documentation:
Enabled: false
# frozen_string_literal コメントは不要
Style/FrozenStringLiteralComment:
Enabled: false
# RSpecのブロックは長くなりがち
Metrics/BlockLength:
Exclude:
- 'spec/**/*'
- 'config/routes.rb'
# RSpecのテストで複数のexpectを許可
RSpec/MultipleExpectations:
Enabled: false
# Strong Parametersのテストスタイルを緩和
Rails/StrongParametersExpect:
Enabled: false
🔨 実際にやってみましょう! RuboCopを実行してみましょう:
# コードスタイルをチェック
docker compose exec web rubocop
出力例(違反がある場合):
Inspecting 15 files
.C..C..........
Offenses:
app/controllers/application_controller.rb:3:1: C: Layout/EmptyLineAfterMagicComment: Add an empty line after magic comments.
def health
^
spec/requests/health_spec.rb:7:11: C: Layout/SpaceInsideHashLiteralBraces: Space inside { missing.
expect(JSON.parse(response.body)).to eq({"status" => "ok"})
^
15 files inspected, 2 offenses detected
- C: Convention(慣習)違反 - コードスタイルの問題
- W: Warning(警告)- 潜在的な問題
- E: Error(エラー)- 構文エラーなど
- F: Fatal(致命的)- 解析できないコード
🔨 実際にやってみましょう! 自動修正を実行します:
# 自動修正可能な問題を修正(-aオプション)
docker compose exec web rubocop -a
出力例:
Inspecting 15 files
.C..C..........
Offenses:
app/controllers/application_controller.rb:3:1: C: [Corrected] Layout/EmptyLineAfterMagicComment: Add an empty line after magic comments.
spec/requests/health_spec.rb:7:11: C: [Corrected] Layout/SpaceInsideHashLiteralBraces: Space inside { missing.
15 files inspected, 2 offenses detected, 2 offenses corrected
rubocop -a: 安全な自動修正のみ実行rubocop -A: より積極的な自動修正(要注意)rubocop --auto-gen-config: 現在の違反を許可する設定ファイルを生成rubocop app/: 特定のディレクトリのみチェック
🔄 CI/CDとの連携
実際の開発現場では、GitHubにプッシュしたときに自動でRuboCopが実行されるよう設定することが一般的です(CI/CD)。これにより:
- コードレビュー前に自動でチェック
- スタイル違反があればマージをブロック
- チーム全体のコード品質を保証
💾 開発ツールの設定をコミット
Better ErrorsとRuboCopの設定が完了したので、コミットします。
🔨 実際にやってみましょう! 開発ツールの設定をコミットします:
# 変更内容を確認
git status
# 開発ツールの設定をコミット
git add config/environments/development.rb .rubocop.yml
git commit -m "開発ツール設定"
8. GitHubへのプッシュとプルリクエスト
📊 これまでのコミット履歴
第2章では、機能ごとに小さくコミットしてきました。
🔨 実際にやってみましょう! コミット履歴を確認してみましょう:
# コミット履歴を確認
git log --oneline -n 5
期待される出力:
a1b2c3d 開発ツール設定
d4e5f6g RSpec追加
h7i8j9k CORS設定
l0m1n2o Gemfile更新
p3q4r5s 第1章完了
a1b2c3dのような文字列はコミットハッシュ(コミットID)と呼ばれます。
- Gitが各コミットに自動的に付ける一意の識別子
- 実際は40文字の長い文字列ですが、通常は先頭7文字で表示
- このIDを使って、特定のコミットに戻ったり、参照したりできます
🌳 ブランチでの作業の進み方
各作業をこまめにコミットすることで、後から「どこで何を変更したか」が一目瞭然になります。
- 各機能の変更が明確に分かれている
- 問題があった時に特定のコミットまで戻れる
- プルリクエストでレビューしやすい
🚀 GitHubリポジトリの作成
最初にGitリポジトリを初期化します。
🔨 実際にやってみましょう! Gitリポジトリを初期化します:
# プロジェクトのルートディレクトリにいることを確認
pwd
# Gitリポジトリを初期化
git init
# 現在の状態を確認
git status
🔨 実際にやってみましょう! GitHubで新しいリポジトリを作成します:
- GitHubにログインし、右上の「+」→「New repository」をクリック

- リポジトリ情報を入力:
- Repository name:
todo-api(または任意の名前) - Description: 「Rails APIとReactで作るTodoアプリ」(任意)
- Public/Private: お好みで選択(画像ではPrivateが選択されています)
- 重要: 「Initialize this repository with」のチェックは全て外す
- Repository name:
画像の赤枠部分で「Private」が選択されています。これは:
- Private: あなたのみ(または招待した人のみ)がアクセス可能
- Public: 誰でもコードを閲覧可能
学習用なら「Private」で問題ありません。ポートフォリオとして公開したい場合は「Public」を選択しましょう。
-
「Create repository」をクリック
-
作成後の画面で表示されるコマンドを確認:

画像の赤枠部分「...or push an existing repository from the command line」は、すでにローカルにGitリポジトリがある場合のコマンドです。第1章でRailsプロジェクトを作成した時点で、自動的にGitリポジトリも作成されているので、この方法を使います。
🔨 実際にやってみましょう! ローカルのGitリポジトリとGitHubを連携させます:
# GitHubリポジトリをリモートとして追加(URLは自分のものに置き換える)
git remote add origin https://github.com/あなたのユーザー名/todo-api.git
# 現在のブランチ名をmainに変更(まだmasterの場合)
git branch -M main
# リモートリポジトリの設定を確認
git remote -v
🚀 GitHubにプッシュ
🔨 実際にやってみましょう! 作業ブランチをGitHubにプッシュします:
# feature/chapter-2ブランチをプッシュ
git push -u origin feature/chapter-2
初回プッシュ時の出力例:
Enumerating objects: 23, done.
Counting objects: 100% (23/23), done.
...
Branch 'feature/chapter-2' set up to track remote branch 'feature/chapter-2' from 'origin'.
📝 プルリクエストの作成
🔨 実際にやってみましょう! GitHubでプルリクエストを作成します:
- ブラウザでGitHubリポジトリを開く
- 「Compare & pull request」ボタンをクリック

- プルリクエストの内容を入力:
Title:
第2章: Rails APIプロジェクトの初期設定
Description:
## 概要
Rails APIモードでプロジェクトの基盤を構築しました。
## 変更内容
- ✅ 必要なGemの追加(認証、CORS、テスト関連)
- ✅ RSpecによるテスト環境の構築
- ✅ CORS設定でフロントエンドからのアクセスを許可
- ✅ 開発効率化ツールの導入(Better Errors、RuboCop)
- ✅ ヘルスチェックエンドポイントの実装
## 動作確認
- [ ] `docker compose up -d` でコンテナが起動すること
- [ ] `http://localhost:3000/health` で `{"status":"ok"}` が表示されること
- [ ] `docker compose exec web rspec` でテストが通ること
## レビューポイント
- Gemfileの依存関係は適切か
- CORS設定のセキュリティは問題ないか
- テスト環境の設定は十分か

- 「Create pull request」ボタンをクリック
✅ マージとmainブランチの更新
プルリクエストが承認されたら(または自分でレビューしたら)、マージします。
本来、プルリクエストは以下の流れで進みます:
- プルリクエストを作成
- 他のメンバーがコードをレビュー
- 修正が必要ならコメントをもらって修正
- 承認(Approve)をもらう
- マージする
今回は学習用なので、自分でレビューしてマージしますが、実際の現場では必ず他の人にレビューしてもらいましょう!
🔨 実際にやってみましょう! GitHubでマージ後、ローカルを更新します:
# mainブランチに切り替え
git checkout main
# リモートの最新情報を取得
git fetch origin
# mainブランチを最新に更新
git pull origin main
# 作業が完了したfeatureブランチを削除(オプション)
git branch -d feature/chapter-2
- 完了したブランチは削除してスッキリ管理
- GitHubには履歴が残るので安心
- 新しい機能は新しいブランチで開発
📋 まとめ
✅ この章で達成したこと
第2章では以下の内容を学びました:
- 🌳 Gitブランチの基本を理解し、実際に使えるようになった
- 🚀 Rails APIモードの特徴と通常モードとの違いを理解した
- 💎 必要なGemを選定し、それぞれの役割を理解した
- 🗄️ データベース接続の仕組みと環境変数の重要性を学んだ
- 🌐 CORSの概念と設定方法を理解した
- 🧪 RSpecによるテスト環境を構築し、実際にテストを書いた
- 🔍 ブラウザとコマンドライン両方でAPIの動作確認ができた
- 📤 プルリクエストの作成からマージまでの流れを体験した
🎯 身についたスキル
- Gitを使った実践的な開発フロー
- Rails APIプロジェクトの初期設定
- Gemの選定と導入
- テスト駆動開発の第一歩
- チーム開発で必要なGitHubの使い方
🚀 次のステップ
次章では、この基盤の上に認証機能を実装します。ユーザーのログイン・ログアウトなど、実際のアプリケーションに必要な機能を作っていきます!
🏃 演習問題
理解を深めるために、以下の課題に挑戦してみましょう:
📝 演習1: APIバージョニング
Rails APIでは、バージョン管理が重要です。以下を実装してください:
/api/v1/healthというパスでアクセスできるようにルーティングを変更Api::V1::ApplicationControllerを作成- テストも合わせて修正
💡 ヒント(クリックで展開)
ルーティングのnamespace:
# config/routes.rb
namespace :api do
namespace :v1 do
# ここにルーティングを書く
end
end
コントローラーの階層:
app/controllers/api/v1/ディレクトリを作成- モジュール構造に注意
✅ 答え(クリックで展開)
# config/routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
get '/health', to: 'application#health'
end
end
end
# app/controllers/api/v1/application_controller.rb
module Api
module V1
class ApplicationController < ActionController::API
def health
render json: { status: 'ok', version: 'v1' }
end
end
end
end
📝 演習2: ヘルスチェックレスポンスの拡張
現在のヘルスチェックAPIに現在時刻を追加してください。
期待されるレスポンス例:
{
"status": "ok",
"timestamp": "2024-01-05T10:30:00+09:00"
}
💡 ヒント(クリックで展開)
現在時刻を取得する方法:
Time.current- Railsのタイムゾーン設定を考慮した現在時刻Time.zone.now- 同じくタイムゾーンを考慮(Time.currentと同じ)
JSONレスポンスに含めるには、healthアクションのrenderメソッドに追加します。
✅ 答え(クリックで展開)
# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
def health
render json: {
status: 'ok',
timestamp: Time.current
}
end
end
別解(ISO8601形式で返す場合):
def health
render json: {
status: 'ok',
timestamp: Time.current.iso8601
}
end
📚 参考資料
より深く学びたい方向け: