Contents
Rails 7 の API モードとは何か
Rails 7 は API 専用モード(--api オプション)を標準で提供しています。ビュー系のミドルウェアや Asset Pipeline が自動的に除外されるため、JSON だけを返すバックエンドとしては構成がシンプルになります。本セクションでは、API モードがどんなメリットをもたらすか、そして「Hotwire」や「import‑map」の取り扱いについて正確に整理します。
- Hotwire は Rails 7 に同梱されているわけではなく、
hotwired-railsgem を追加しなければ利用できません。 - import‑map は
--importmapフラグで生成できるオプションです。このフラグは Rails 7.0 以降で使用可能です(7.1 だけの限定ではありません)。
以上を踏まえて、API モードが提供する「軽量化」の実体と、導入時に注意すべきポイントを見ていきます。
API モードでアプリケーションを作成する手順と主要オプション
このセクションでは rails new --api の基本的な使い方と、実務で頻出するフラグの意味を解説します。プロジェクト開始時に不要なコンポーネントを除外できるので、後から手作業で削除する手間が省けます。
rails new --api の基本形
--api フラグは Rails アプリケーションの設定ファイル(config/application.rb) に自動的に config.api_only = true を書き込み、ビュー・アセット関連ミドルウェアを最小構成にします。以下は最もシンプルなコマンド例です。
|
1 2 |
rails new my_api_app --api |
この状態だけでも API サーバーとして動作しますが、実際のプロジェクトではテストフレームワークやメール機能など不要なものをさらにスキップすることが多いです。
実務でよく使うオプション
| オプション | 用途・効果 | 例 |
|---|---|---|
-T / --skip-test |
MiniTest を除外し、RSpec など別のテストフレームワークを採用したいときに使用 | rails new my_api --api -T |
--skip-action-mailbox |
Action Mailbox のコードとミドルウェアを削除(メール受信は不要なケース) | rails new my_api --api --skip-action-mailbox |
--skip-action-text |
Rich Text 機能(Action Text)を除外 | rails new my_api --api --skip-action-text |
--skip-active-record |
ActiveRecord を使わない場合に ORM 関連コードとミドルウェアを削除 | rails new my_api --api --skip-active-record |
--database=postgresql |
データベースのデフォルト設定を PostgreSQL に変更(MySQL や SQLite でも同様) | rails new my_api --api --database=postgresql |
--importmap |
Import‑map を使用したフロントエンド資産管理を有効化(Rails 7.0 以降で利用可能) | rails new my_app --importmap |
ポイント:API モードでもデータベースは必須ではありません。外部サービスや NoSQL データストアだけで完結させる場合は
--skip-active-recordが有効です。
設定ファイル(application.rb / environments/*.rb)で意識すべき項目
API アプリは ミドルウェアの削減 と 環境別の CORS・ロギング設定 がパフォーマンスとセキュリティに直結します。ここでは実務的に使われる典型的なコード例を示し、何が「必要」かを明確にします。
config/application.rb におけるミドルウェア調整
API モードはデフォルトで config.api_only = true が設定されますが、実際にはまだ Cookie や Session などのミドルウェアがロードされています。不要なものを削除するとリクエストごとの呼び出しコストが減少します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# config/application.rb module MyApiApp class Application < Rails::Application # Rails 7 のデフォルト設定を読み込む(7.0/7.1 共通) config.load_defaults 7.0 # API モードのフラグ config.api_only = true # 不要ミドルウェアの削除例 config.middleware.delete ActionDispatch::Cookies config.middleware.delete ActionDispatch::Session::CookieStore config.middleware.delete Rack::MethodOverride # 必要なら残す end end |
効果:上記のように Cookie 関連を外すだけでも、メモリ使用量が数 MB 減り、シンプルなステートレス API に適した構成になります。
環境別設定(development / production)
開発環境と本番環境では CORS の許可範囲 と ログレベル を分けることがベストプラクティスです。以下は両環境での典型的な設定例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# config/environments/development.rb Rails.application.configure do # デバッグ情報を最大化 config.log_level = :debug config.consider_all_requests_local = true # 開発時は全オリジンを許可(ローカルフロントエンドとの連携用) config.middleware.insert_before 0, Rack::Cors do allow do origins '*' resource '*', headers: :any, methods: %i[get post put patch delete options head] end end end # config/environments/production.rb Rails.application.configure do # 本番は情報漏洩防止のためログレベルを抑える config.log_level = :info config.consider_all_requests_local = false # 許可されたオリジンだけに絞る config.middleware.insert_before 0, Rack::Cors do allow do origins 'https://frontend.example.com', 'https://admin.example.com' resource '*', headers: :any, methods: %i[get post put patch delete options head], expose: ['Authorization'], max_age: 600 end end # JSON エラーレスポンスを統一 config.debug_exception_response_format = :api end |
ポイント:
Rack::Corsの設定は「開発時はワイルドカード、本番では明示的に列挙」の原則で管理すると、意図しないオリジンからのアクセスを防げます。
CORS とシリアライザ選択肢
API が外部クライアント(SPA、モバイルアプリ、他サービス)とやり取りする際に必須となる CORS 設定と、返却データのフォーマットを整える シリアライザ の選び方について解説します。
rack-cors の導入手順
- Gemfile に追加し bundle install
- 初期化ファイルでミドルウェアとして組み込むだけで、環境ごとに細かい制御が可能です。
|
1 2 3 |
# Gemfile gem 'rack-cors', '~> 1.2' |
|
1 2 3 4 5 6 7 8 9 10 11 |
# config/initializers/cors.rb Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins ENV.fetch('CORS_ORIGINS') { 'http://localhost:3000' } resource '*', headers: :any, methods: %i[get post put patch delete options head], expose: ['Authorization'] end end |
ベストプラクティス:
CORS_ORIGINSは環境変数で管理し、ステージング・本番それぞれに異なる値を設定する。
シリアライザの比較と選定基準
| 項目 | Active Model Serializers (AMS) | jsonapi-serializer (fast_jsonapi) |
|---|---|---|
| 標準化 | 任意の JSON 構造に柔軟対応 | JSON:API 1.0 に完全準拠 |
| パフォーマンス | オブジェクト単位でシリアライズ(中程度) | ハッシュ構築を最適化し高速 |
| 学習コスト | Rails 標準的 DSL が直感的 | 宣言的 DSL だがドキュメント充実 |
| 拡張性 | メソッドオーバーライドで自由度高い | アトリビュート・関係を宣言的に記述 |
外部サービスと JSON:API でやり取りする場合は jsonapi-serializer が推奨されます。社内向けのシンプルなエンドポイントだけなら AMS の方が導入ハードルが低いです。
AMS の基本設定例
|
1 2 3 4 5 6 7 8 9 |
# Gemfile gem 'active_model_serializers', '~> 0.14' # app/serializers/user_serializer.rb class UserSerializer < ActiveModel::Serializer attributes :id, :email, :created_at has_many :posts end |
jsonapi-serializer の基本設定例
|
1 2 3 4 5 6 7 8 9 10 |
# Gemfile gem 'jsonapi-serializer', '~> 3.0' # app/serializers/user_serializer.rb class UserSerializer include JSONAPI::Serializer attributes :email, :created_at has_many :posts end |
認証・認可の実装例とテスト戦略
トークンベース認証は API 開発において必須です。ここでは Devise Token Auth と 自前 JWT 実装 の比較を行い、メリット・デメリットだけでなくセキュリティ上の注意点も併せて示します。そのうえで、RSpec と Minitest によるテストコード例を提示し、実務で再利用できる形にまとめます。
Devise Token Auth の特徴と適用シーン
| 観点 | 内容 |
|---|---|
| 導入コスト | devise と devise_token_auth をインストールすれば、ユーザー登録・ログイン・トークン更新 API が自動生成される。 |
| 機能範囲 | トークンのローテーション、複数デバイス対応、期限設定、ヘッダー/クエリパラメータどちらでも利用可能。 |
| 拡張性 | Devise の Warden フックを使えばカスタム認可ロジックも容易に追加できる。 |
| 運用上の注意 | デフォルトではトークンは DB に保存しない(stateless)。リフレッシュトークンが必要な場合は別途実装する必要がある。 |
インストール手順
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Gemfile gem 'devise' gem 'devise_token_auth' bundle install # Devise のセットアップ rails generate devise:install rails generate devise User # Token Auth のセットアップ(User モデルにマウント) rails generate devise_token_auth:install User auth |
生成された /auth 名前空間のエンドポイントは次のとおりです。
| HTTP メソッド | パス | 目的 |
|---|---|---|
POST |
/auth/sign_in |
ログイン(トークン取得) |
DELETE |
/auth/sign_out |
トークン失効 |
PUT/PATCH |
/auth |
アカウント情報更新 |
GET |
/auth/validate_token |
現在のトークンが有効か確認 |
セキュリティ補足:トークンは Bearer ヘッダーで送信し、HTTPS を必ず使用してください。
config/initializers/devise_token_auth.rbでtoken_lifespan(例: 2 weeks)やbatch_request_buffer_throttleを調整することでリプレイ攻撃のリスクを低減できます。
JWT を自前実装した場合のトレードオフ
| 項目 | 説明 |
|---|---|
| 柔軟性 | ペイロードに独自クレーム(ロール、組織 ID 等)を自由に追加できる。 |
| 運用コスト | トークンの署名検証・失効管理は開発者側で実装する必要がある。 |
| アルゴリズム選択 | HS256(対称鍵)は実装が簡単だが、キー漏洩時に全トークンが危険になる。RS256(非対称鍵)なら公開鍵だけを配布でき、安全性が高いが鍵管理がやや複雑。 |
| 失効 / 再発行 | JWT はステートレスなので、サーバ側で即時失効させるにはブラックリストテーブル等を別途用意する必要がある。 |
シンプルな実装例(HS256)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# Gemfile gem 'jwt' # app/controllers/api/v1/auth_controller.rb class Api::V1::AuthController < ApplicationController skip_before_action :verify_authenticity_token # POST /api/v1/login def login user = User.find_by(email: params[:email]) if user&.authenticate(params[:password]) payload = { sub: user.id, exp: 24.hours.from_now.to_i } token = JWT.encode(payload, Rails.application.secret_key_base, 'HS256') render json: { token: token }, status: :ok else render json: { error: 'Invalid credentials' }, status: :unauthorized end end # GET /api/v1/me def me @current_user = authenticate_jwt! render json: { id: @current_user.id, email: @current_user.email } end private # JWT 認証ヘルパー(失効や期限切れは例外でハンドリング) def authenticate_jwt! auth_header = request.headers['Authorization'] token = auth_header.to_s.split(' ').last decoded = JWT.decode(token, Rails.application.secret_key_base, true, algorithm: 'HS256')[0] User.find(decoded['sub']) rescue JWT::ExpiredSignature render json: { error: 'Token expired' }, status: :unauthorized && return rescue JWT::DecodeError, ActiveRecord::RecordNotFound render json: { error: 'Invalid token' }, status: :unauthorized && return end end |
注意点
expクレームでトークン有効期限を必ず設定し、短め(例:1 hour)にすると漏洩リスクが減ります。
本番環境では HS256 よりも RS256 を選択し、プライベートキーはRails.application.credentialsに安全に格納してください。
テスト戦略:RSpec と Minitest の実装例
API エンドポイントのテストでは 認証ヘッダー と JSON 構造 の両方を検証します。以下は代表的なリクエストスペックです。
RSpec(request spec)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# spec/requests/api/v1/posts_spec.rb require 'rails_helper' RSpec.describe 'Posts API', type: :request do let(:user) { create(:user) } let(:token) { JwtService.encode(user_id: user.id) } # JWT 発行ユーティリティ例 let(:headers){ { 'Authorization' => "Bearer #{token}" } } describe 'GET /api/v1/posts' do before { create_list(:post, 3, author: user) } it 'returns a list of posts with correct JSON structure' do get '/api/v1/posts', headers: headers expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json['data'].size).to eq(3) expect(json['data'].first['attributes']).to include('title') end end end |
Minitest(Integration test)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# test/integration/api/v1/posts_test.rb require "test_helper" class Api::V1::PostsTest < ActionDispatch::IntegrationTest setup do @user = users(:one) @token = JwtService.encode(user_id: @user.id) @headers = { 'Authorization' => "Bearer #{@token}" } end test "should get index" do get api_v1_posts_url, headers: @headers, as: :json assert_response :success body = JSON.parse(response.body) assert_includes body['data'].first['attributes'], 'title' end end |
テストでのベストプラクティス
as: :jsonオプションを付けてリクエストヘッダーにContent-Type: application/jsonを自動設定。
トークン生成はテスト専用のサービスオブジェクト(例:JwtService.encode)で一元管理し、キーや有効期限が変更されたときにもテストコードを修正せずに済む。
デプロイ手順と本番向けパフォーマンスチューニング
API アプリは 軽量化 と スケーラビリティ が求められます。ここでは Heroku と Docker の代表的なデプロイ方法を示し、さらに本番環境で有効なミドルウェア最小化・Eager Loading 設定について解説します。
Heroku でのシンプルなデプロイ
Procfileに Puma 起動コマンドを書くだけ- 必要な環境変数(
RAILS_ENV,SECRET_KEY_BASE,DATABASE_URL)を CLI または Dashboard から設定
|
1 2 3 |
# Procfile web: bundle exec puma -C config/puma.rb |
|
1 2 3 4 5 |
# デプロイ手順例 heroku create myapi-app --stack=container # Docker コンテナスタックを利用(任意) git push heroku main heroku run rails db:migrate |
ポイント:Heroku の
containerスタックは自前の Dockerfile をそのまま使用できるため、ローカルと本番で同一イメージを走らせられます。
Docker で本番イメージをビルドするベストプラクティス
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# ---------- Builder Stage ---------- FROM ruby:3.2-alpine AS builder WORKDIR /app # ビルドに必要なパッケージだけインストール RUN apk add --no-cache build-base postgresql-dev nodejs npm git COPY Gemfile* ./ RUN bundle config set deployment 'true' && \ bundle install --without development test # ---------- Runtime Stage ---------- FROM ruby:3.2-alpine WORKDIR /app # Builder で作った gem をコピー COPY --from=builder /usr/local/bundle/ /usr/local/bundle/ # ソースコード全体をコピー COPY . . # Puma の設定ファイルがある前提 EXPOSE 3000 CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"] |
|
1 2 3 4 5 6 |
docker build -t myapi:prod . docker run -d -p 3000:3000 \ --env-file=.env.production \ --restart=unless-stopped \ myapi:prod |
利点:マルチステージビルドにより、コンパイルツールや開発用ヘッダーが最終イメージに残らないため、サイズは約 120 MB 前後(Alpine ベース)に抑えられます。
本番向けパフォーマンスチューニング
| チューニング項目 | 方法 | 効果の期待値 |
|---|---|---|
| Eager Load | config.eager_load = true(production.rb) |
起動時に全クラスをロードし、リクエストごとの遅延ロードを防止。Cold start が数秒短縮されることが多い |
| ミドルウェア削除 | 不要な Rack::Sendfile, ActionDispatch::Cookies などを config.middleware.delete で除外 |
メモリ使用量と CPU サイクルが数%削減 |
| HTTP 圧縮 | Rack::Deflater をミドルウェアに追加 |
JSON レスポンスサイズが 30 % 前後圧縮され、帯域コストが低減 |
| N+1 クエリ回避 | includes, preload, eager_load を適切に使用 |
データベースアクセス数が劇的に削減され、レイテンシが 20‑30 % 改善 |
production.rb のサンプル設定
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# config/environments/production.rb Rails.application.configure do # コードを eager load(API アプリでは推奨) config.eager_load = true # 不要ミドルウェアの除去例 config.middleware.delete ActionDispatch::Cookies config.middleware.delete Rack::Sendfile # 静的ファイル配信が不要なら削除 # 圧縮ミドルウェアを有効化 config.middleware.use Rack::Deflater # キャッシュヘッダー(クライアント側キャッシュ)設定例 config.public_file_server.headers = { 'Cache-Control' => "public, max-age=#{2.days.to_i}" } end |
実務でのチェックリスト
1.bundle exec rails routesで API 用のエンドポイントだけが残っているか確認。
2.rails s -e productionをローカルで起動し、ps aux | grep pumaでプロセス数とメモリ使用量を測定。
3. 本番環境のロードテスト(例:k6, ApacheBench)で 平均応答時間 と スループット を記録し、チューニング前後で比較。
まとめと次に取るべきアクション
- API モードはデフォルトでミドルウェアが削減された軽量構成です。Hotwire は別途 gem が必要である点を忘れずに。
rails new --apiに加えるオプション(--skip-active-record,--database=postgresql,--importmapなど)でプロジェクト要件に合わせた最小構成を作ります。- CORS は
rack-corsと環境変数で管理し、開発と本番で許可オリジンを分けます。 - シリアライザ は AMS か jsonapi‑serializer を選択し、外部 API との連携要件に合わせて実装してください。
- 認証は Devise Token Auth(フル機能)と 自前 JWT(柔軟性)を比較し、トークン有効期限・アルゴリズム・失効管理の設計を必ず行うことが重要です。
- テストは RSpec の request spec か Minitest の統合テストで 認証ヘッダーと JSON 構造 を検証し、CI パイプラインに組み込んでください。
- デプロイは Heroku または Docker を選択し、ミドルウェア最小化・Eager Loading・HTTP 圧縮 で本番パフォーマンスを最大化します。
次のステップ:この記事を参考に実際に
rails new my_api --api -Tでプロジェクトを作成し、上記設定項目を順に適用してみましょう。CI に RSpec のリクエストスペックを追加したら、ローカルでdocker compose up(または Heroku デプロイ)を実行し、パフォーマンス測定ツールで レスポンスタイム と スループット を確認してください。改善点が見つかれば、前節のチューニングリストに沿ってミドルウェアやキャッシュ設定を微調整すると、実運用レベルの高速 API が手に入ります。