📚

Next.js + Rails (APIモード)アプリケーションの環境構築

2025/02/22に公開

はじめに

フロントエンドがNext.js、バックエンドがRuby on RailsのAPIモードで構成するアプリケーションの開発環境をまとめました。
バックエンドはDockerを使用して環境を統一し、依存関係の管理やデプロイを容易にしています。
モノレポでの管理やCI/CDでフロントエンド・バックエンドで個別のワークフローを実行できるようにもしています。

https://github.com/wakiy1031/template_Next.js-15_Rails-8

技術スタック

バックエンド

  • Ruby: 3.2.2
  • Ruby on Rails: 8.0.1(API モード)
  • PostgreSQL: 14
  • Redis: 7
  • Sidekiq: バックグラウンドジョブ処理

フロントエンド

  • Next.js(App Router)
  • TypeScript: ^5.0.0
  • Node.js: ^20.0.0
  • TailwindCSS
  • Zustand(状態管理)
  • Jest + React Testing Library

インフラストラクチャ

  • Docker & Docker Compose
  • GitHub Actions(CI/CD)

プロジェクト構成

本プロジェクトはモノレポ(単一リポジトリ)で管理されており、以下のような構成となっています:

my-app/
├── .github/
│   └── workflows/          # GitHub Actions設定
│       ├── frontend.yml    # フロントエンド用CI/CD
│       └── backend.yml     # バックエンド用CI/CD
├── frontend/               # Next.jsアプリケーション
└── backend/               # Rails APIアプリケーション

モノレポ構成のメリット

  1. コードの一元管理

    • フロントエンド・バックエンドの変更履歴を一箇所で管理
    • 依存関係の管理が容易
    • デプロイの同期が取りやすい
  2. 開発効率の向上

    • プロジェクト全体を一度にクローン可能
    • 横断的な変更が容易
    • チーム間の連携がスムーズ
  3. CI/CD の柔軟な制御

    • フロントエンド・バックエンドで個別のワークフローを実行可能
    • 変更があった部分のみビルド・テストを実行

開発環境のセットアップ

前提条件

  • Docker
  • Docker Compose
  • Git

1. プロジェクトのセットアップ

Dockerfile の作成

backend/Dockerfile.devを作成:

ARG RUBY_VERSION=3.2.2
FROM docker.io/library/ruby:$RUBY_VERSION-slim

# 必要なパッケージをインストール
RUN apt-get update -qq && apt-get install -y \
  build-essential \
  libpq-dev \
  postgresql-client

# アプリケーションディレクトリを作成
WORKDIR /app

# Gemfile と Gemfile.lock をコピーし、依存関係をインストール
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
RUN bundle install

# アプリケーションの全ファイルをコンテナ内にコピー
COPY . /app

# サーバー起動
CMD ["rails", "server", "-b", "0.0.0.0"]

Docker Compose の設定

backend/docker-compose.ymlを作成:

version: "3"

services:
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/rails
      - ruby-bundle-cache:/bundle
    ports:
      - "3000:3000"
    environment:
      RAILS_ENV: development
      DATABASE_HOST: db
      DATABASE_USERNAME: postgres
      DATABASE_PASSWORD: postgres
    depends_on:
      - db
      - redis

  db:
    image: postgres:14
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - "5432:5432"

  redis:
    image: redis:7
    ports:
      - "6379:6379"

  sidekiq:
    build: .
    command: bundle exec sidekiq
    volumes:
      - .:/rails
      - ruby-bundle-cache:/bundle
    environment:
      RAILS_ENV: development
      DATABASE_HOST: db
      DATABASE_USERNAME: postgres
      DATABASE_PASSWORD: postgres
    depends_on:
      - db
      - redis

volumes:
  postgres_data:
  ruby-bundle-cache:
    external: true

2. データベース設定

backend/config/database.ymlを設定:

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host: <%= ENV.fetch("DATABASE_HOST") { "db" } %>
  username: <%= ENV.fetch("DATABASE_USERNAME") { "postgres" } %>
  password: <%= ENV.fetch("DATABASE_PASSWORD") { "postgres" } %>

development:
  <<: *default
  database: focus_forge_development

test:
  <<: *default
  database: focus_forge_test

production:
  primary: &primary_production
    <<: *default
    database: app_production
    username: app
    password: <%= ENV["APP_DATABASE_PASSWORD"] %>
  cache:
    <<: *primary_production
    database: app_production_cache
    migrations_paths: db/cache_migrate
  queue:
    <<: *primary_production
    database: app_production_queue
    migrations_paths: db/queue_migrate
  cable:
    <<: *primary_production
    database: app_production_cable
    migrations_paths: db/cable_migrate

3. Gemfile の設定

backend/Gemfileの主要な依存関係:

source "https://rubygems.org"

gem "rails", "~> 8.0.1"
gem "pg", "~> 1.1"
gem "puma", ">= 5.0"
gem "tzinfo-data", platforms: %i[ windows jruby ]
gem "bootsnap", require: false
gem "solid_cache"
gem "solid_queue"
gem "solid_cable"
gem "kamal", require: false
gem "thruster", require: false

group :development, :test do
  gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
  gem "brakeman", require: false
  gem "rubocop-rails-omakase", require: false
end

4. アプリケーションの起動

# Dockerイメージのビルド
docker compose build

# コンテナの起動
docker compose up -d

# データベースの作成とマイグレーション
docker compose exec web rails db:create db:migrate

localhost:3000 にアクセスでこの画面が出れば完了です。

4. フロントエンド環境のセットアップ

4.1 Next.js プロジェクトの作成

# プロジェクトの作成
npx create-next-app@latest frontend
cd frontend

# 以下の質問に対して回答
✔ Would you like to use TypeScript? Yes
✔ Would you like to use ESLint? Yes
✔ Would you like to use Tailwind CSS? Yes
✔ Would you like to use `src/` directory? Yes
✔ Would you like to use App Router? (recommended) Yes
✔ Would you like to customize the default import alias (@/*)? Yes

4.2 依存関係のインストール

npm install

4.3 ESLint の設定

.eslintrc.jsonを作成:

{
  "extends": ["next/core-web-vitals"],
  "plugins": ["@typescript-eslint"],
  "rules": {
    "@typescript-eslint/no-unused-vars": [
      "error",
      { "argsIgnorePattern": "^_" }
    ],
    "@typescript-eslint/no-explicit-any": "error",
    "no-console": ["warn", { "allow": ["warn", "error"] }]
  }
}

4.4 TypeScript の設定

tsconfig.jsonを更新:

{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    "**/*.mjs",
    ".next/types/**/*.ts"
  ],
  "exclude": ["node_modules"]
}

4.5 開発サーバーの起動

# 開発サーバーの起動
npm run dev

localhost:3001 にアクセスでこの画面が出れば完了です。

4.6 ビルドとテスト

# 型チェック
npm run type-check

# リントチェック
npm run lint

# ビルド
npm run build

# プロダクションモードでの起動
npm run start

トラブルシューティング

バックエンド(Rails)

1. docker compose upでポート 3000 が使用中のエラー

Error response from daemon: Ports are not available: exposing port TCP 0.0.0.0:3000 -> 0.0.0.0:0: listen tcp 0.0.0.0:3000: bind: address already in use

解決方法:

  1. 実行中のコンテナを確認
docker ps
  1. 該当のポートを使用しているコンテナを停止
docker stop [CONTAINER_ID]

2. webサービスが見つからないエラー

Error response from daemon: service 'web' is not running container

解決方法:

  1. コンテナの状態を確認
docker compose ps
  1. サービスを再起動
docker compose down
docker compose up -d

フロントエンド(Next.js)

1. ESLint の設定の競合

Plugin "react-hooks" was conflicted between ".eslintrc.json » plugin:react-hooks/recommended" and ".eslintrc.json » eslint-config-next/core-web-vitals"

解決方法:
.eslintrc.jsonを以下のように簡素化:

{
  "extends": ["next/core-web-vitals"],
  "plugins": ["@typescript-eslint"],
  "rules": {
    "@typescript-eslint/no-unused-vars": [
      "error",
      { "argsIgnorePattern": "^_" }
    ],
    "@typescript-eslint/no-explicit-any": "error",
    "no-console": ["warn", { "allow": ["warn", "error"] }]
  }
}

2. TypeScript の設定で Next.js の型定義が見つからないエラー

We detected TypeScript in your project and reconfigured your tsconfig.json file

解決方法:
tsconfig.jsoninclude.next/types/**/*.tsを追加:

{
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    "**/*.mjs",
    ".next/types/**/*.ts"
  ]
}

共通のトラブルシューティング手順

  1. ログの確認
# Dockerコンテナのログ
docker compose logs -f [SERVICE_NAME]

# Next.jsの開発サーバーログ
npm run dev
  1. 依存関係の更新
# Railsの依存関係
bundle update

# Next.jsの依存関係
npm update
  1. キャッシュのクリア
# Dockerのキャッシュクリア
docker compose down
docker system prune -f

# Next.jsのキャッシュクリア
rm -rf .next
npm clean-install
  1. 設定ファイルの検証
  • docker-compose.ymlの環境変数が正しく設定されているか確認
  • .envファイルが適切に配置されているか確認
  • 各種設定ファイル(tsconfig.json, .eslintrc.jsonなど)の構文エラーがないか確認

さいごに

以上が環境構築になります。
ぜひテンプレートリポジトリを使ってみてうまくいったよという方は
この記事にいいねとテンプレートリポジトリのほうにStar付けていただけるとうれしいです!
https://github.com/wakiy1031/template_Next.js-15_Rails-8
もしうまくいかなかったという方がいればコメントいただけると幸いです。

Discussion