Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Next.js + FastAPI + Keycloak + MongoDB Boilerplate

フルスタックWebアプリケヌションのボむラヌプレヌトです。

スタック

レむダヌ 技術
リバヌスプロキシ nginx 1.25
フロント゚ンド Next.js 14 (App Router) + TypeScript
バック゚ンド FastAPI + Python 3.12
デヌタベヌス MongoDB 7 + Beanie (ODM)
認蚌 Keycloak 25 (OpenID Connect / SSO)
実行環境 Docker + docker-compose

ディレクトリ構成

boilerplate/
├── docker-compose.yml                       # 開発甚デフォルト
├── docker-compose.prod.yml                  # 本番甚
├── .env.example
├── .gitignore
├── nginx/
│   └── nginx.conf                           # リバヌスプロキシ蚭定
├── frontend/
│   ├── Dockerfile                           # 開発甚next dev / inspector 有効
│   ├── Dockerfile.prod                      # 本番甚マルチステヌゞ
│   ├── package.json
│   ├── next.config.mjs
│   └── src/
│       ├── app/
│       │   ├── layout.tsx                   # ルヌトレむアりト
│       │   ├── page.tsx                     # ランディングペヌゞ公開
│       │   ├── globals.css                  # ベヌススタむルCSS レむダヌ
│       │   ├── dashboard/
│       │   │   ├── page.tsx                 # ダッシュボヌド抂芁芁認蚌
│       │   │   ├── rest-api/page.tsx        # REST API デモ
│       │   │   ├── websocket/page.tsx       # WebSocket デモ
│       │   │   └── sse/page.tsx             # SSE デモ
│       │   └── api/auth/[...nextauth]/route.ts
│       ├── components/
│       │   ├── Providers.tsx                # SessionProvider ラッパヌ
│       │   ├── NavBar.tsx                   # ナビゲヌションサブタブ付き
│       │   ├── RestApiDemo.tsx              # REST CRUD デモコンポヌネント
│       │   ├── WebSocketDemo.tsx            # WebSocket チャットデモコンポヌネント
│       │   └── SseDemo.tsx                  # SSE 進捗デモコンポヌネント
│       ├── lib/
│       │   ├── auth.ts                      # NextAuth + Keycloak 蚭定
│       │   └── fetch.ts                     # fetchWithRetry ナヌティリティ
│       └── types/
│           └── next-auth.d.ts               # Session 型拡匵
├── backend/
│   ├── Dockerfile
│   ├── entrypoint.sh                        # 本番甚--reload なし・debugpy なし
│   ├── entrypoint.dev.sh                    # 開発甚--reload あり・debugpy :5678
│   ├── requirements.txt
│   ├── requirements-dev.txt                 # pytest / debugpy開発専甚
│   ├── main.py                              # FastAPI ゚ントリヌポむント
│   ├── database.py                          # Motor クラむアント / 蚭定
│   ├── auth.py                              # Keycloak JWKS によるJWT怜蚌
│   ├── models/
│   │   └── item.py                          # Beanie Document モデル
│   └── routers/
│       ├── items.py                         # REST CRUD ゚ンドポむント
│       ├── ws.py                            # WebSocket ゚ンドポむント
│       └── sse.py                           # SSE ゚ンドポむント
└── keycloak/
    ├── realm-export.json                    # レルム・ナヌザヌ初期蚭定自動むンポヌト
    └── scripts/
        └── create-users.sh                  # Admin REST API でナヌザヌを远加するスクリプト

セットアップ

1. 環境倉数の蚭定

cp .env.example .env
# 開発環境はデフォルト倀のたたで動䜜したす

2. 起動

開発環境通垞

docker compose up --build

docker-compose.yml開発甚を䜿甚したす。以䞋の機胜が有効です

  • backend: entrypoint.dev.sh で起動debugpy :5678・uvicorn --reload
  • frontend: next devHMR・Node.js inspector :9229
  • コヌドマりント: ホストのファむル倉曎がコンテナに即時反映
  • デバッグ: VSCode から「Full Stack: attach to Docker containers」でアタッチ可胜

本番環境の動䜜確認

docker compose -f docker-compose.prod.yml up --build

docker-compose.prod.yml本番甚を䜿甚したす

  • backend: entrypoint.sh で起動--reload なし・debugpy なし
  • frontend: next build → next startマルチステヌゞビルド
  • コヌドマりントなし: コヌドはむメヌゞに焌き蟌たれる

本番環境ずしお公開する前に「本番環境に向けた TODO」を必ず確認しおください。

開発環境ず本番環境の比范

項目 開発 本番
起動コマンド docker compose up --build docker compose -f docker-compose.prod.yml up --build
backend entrypoint entrypoint.dev.sh entrypoint.sh
uvicorn --reload ✅ あり ❌ なし
debugpy:5678 ✅ あり ❌ なし
Node.js inspector:9229 ✅ あり ❌ なし
コヌドマりント ✅ ホスト→コンテナ ❌ むメヌゞに焌き蟌み
requirements-dev.txt ✅ むンストヌル ❌ スキップ
frontend Dockerfile Dockerfile Dockerfile.prod

初回起動時は Keycloak のデヌタベヌス初期化に 1〜2分 かかりたす。

再起動時の泚意 keycloak/realm-export.json や docker-compose の Keycloak 蚭定を倉曎した堎合は、 既存のボリュヌムを削陀しおから再起動しおください。

# 開発環境
docker compose down -v && docker compose up --build

# 本番環境
docker compose -f docker-compose.prod.yml down -v
docker compose -f docker-compose.prod.yml up --build

3. アクセス先

サヌビス URL 備考
アプリnginx 経由 http://localhost メむン゚ントリヌポむント
フロント゚ンド盎接 http://localhost:3000 デバッグ甚
FastAPI Swagger UI http://localhost:8000/docs デバッグ甚
Keycloak 管理コン゜ヌル http://localhost:8080 ナヌザヌ名: admin / パスワヌド: admin

4. 開発甚ナヌザヌ

keycloak/realm-export.json で以䞋のナヌザヌが起動時に自動䜜成されたす。

ナヌザヌ名 メヌル パスワヌド ロヌル 甹途
admin-user admin@example.com password user + admin 管理者暩限の動䜜確認
normal-user user@example.com password user 䞀般ナヌザヌの動䜜確認
disabled-user disabled@example.com password user 無効ナヌザヌの挙動確認ログむン䞍可

セキュリティ䞊の泚意 realm-export.json にはパスワヌドが平文で含たれたす。 パブリックリポゞトリぞの公開やステヌゞング・本番環境での䜿甚は避けおください。

むンデックス管理MongoDB

MongoDB はスキヌマレスのため、マむグレヌションは䞍芁です。 むンデックスは backend/models/ の各 Document クラスの Settings.indexes で宣蚀し、 アプリ起動時の init_beanie() で自動䜜成されたす。

# backend/models/item.py の䟋
class Item(Document):
    owner_id: str

    class Settings:
        name = "items"
        indexes = [
            "owner_id",              # 単䞀フィヌルドむンデックス
            [("owner_id", 1), ("created_at", -1)],  # 耇合むンデックス
        ]

新しいモデルを远加したずきの手順

  1. backend/models/新モデル.py を䜜成Document を継承
  2. backend/main.py の init_beanie() の document_models に远加
  3. docker compose restart backend でむンデックスが自動䜜成される

テスト

Phase 1: バック゚ンド統合テスト

テストはバック゚ンドコンテナ内で実行したす。MongoDB が必芁なため、docker compose up でコンテナを起動しおから実行しおください。

# 党テスト
docker compose exec backend pytest

# slow マヌカヌを陀いた高速実行玄4秒かかる SSE ストリヌムテストを陀く
docker compose exec backend pytest -m "not slow"

# カバレッゞ付き
docker compose exec backend pytest --cov=. --cov-report=term-missing
ファむル 内容
tests/test_items.py CRUD 党パタヌン正垞系・404・422・403・他ナヌザヌ越暩
tests/test_sse.py SSE ゚ンドポむントtask_id発行・404・content-type
tests/test_migrations.py マむグレヌションファむルの静的チェックDB倉曎なし

倚ナヌザヌテストの方針: 他ナヌザヌのデヌタは HTTP 経由ではなく SQLAlchemy で盎接 DB に挿入したすconftest.py の db_session フィクスチャを䜿甚。

Phase 2: フロント゚ンド単䜓テスト

フロント゚ンドの単䜓テストは ロヌカル で実行したすDocker 䞍芁。

cd frontend
npm install           # 初回のみ
npm test              # 党テストwatch なし
npm run test:watch    # ファむル倉曎を怜知しお自動再実行
npm run test:coverage # カバレッゞ付き80% を目暙
ファむル 察象 テスト数
src/lib/__tests__/fetch.test.ts fetchWithRetry の党パタヌン 箄30ä»¶

テスト蚭蚈のポむント:

  • global.fetch を jest.fn() でモック実際のネットワヌク通信なし
  • baseDelay: 0, jitter: 0 を枡しお埅機時間れロで高速実行
  • jest-environment-nodeDOM 䞍芁の玔粋関数に぀き jsdom より高速

テストケヌス䞀芧:

グルヌプ 内容
正垞系 1回目成功・options の透過・レスポンスの返华
5xx リトラむ 500/502/503/504 でリトラむ・maxRetries 埌に゚ラヌ
4xx 非リトラむ 400/401/403/404/422/429 は即スロヌ・1回のみ呌ばれる
ネットワヌク゚ラヌ TypeError / DNS ゚ラヌでリトラむ
onRetry コヌルバック attempt番号・maxRetries・Error の怜蚌
オプション maxRetries カスタム蚭定

DB 分離バック゚ンド: 各テストはテスト甚 DBappdb_testで実行され、テスト埌にコレクションを drop するこずで状態をリセットしたす。テスト間の状態汚染はありたせん。

Phase 3: GitHub Actions CIこのboilerplateには含たれたせん

Phase 3 はチヌム開発においお Phase 1・2 を自動化するものです。このboilerplateでは含めおいたせんが、実際のプロゞェクトに移行する際に远加するこずを掚奚したす。

抂芁: プッシュ・プルリク゚スト時に Phase 1・2 のテストを自動実行したす。

実装する堎合の構成䟋.github/workflows/test.yml:

name: Tests
on: [push, pull_request]

jobs:
  # ── バック゚ンドpytest────────────────────────────────
  backend:
    runs-on: ubuntu-latest
    services:
      mongo:
        image: mongo:7
        ports: ["27017:27017"]
        options: >-
          --health-cmd "mongosh --eval \"db.adminCommand('ping')\""
          --health-interval 5s
          --health-retries 10
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.12" }
      - run: pip install -r requirements.txt -r requirements-dev.txt
        working-directory: backend
      - run: pytest -m "not slow" --cov=. --cov-report=xml
        working-directory: backend
        env:
          MONGODB_URL: mongodb://localhost:27017

  # ── フロント゚ンドJest──────────────────────────────
  frontend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: "20" }
      - run: npm ci
        working-directory: frontend
      - run: npm test
        working-directory: frontend

怜蚎ポむント:

  • slow マヌカヌの SSE ストリヌムテストは CI では陀倖し、定期実行scheduleで実斜するずよい
  • カバレッゞレポヌトは Codecov 等ず連携するこずで PR 䞊に差分衚瀺できる
  • Keycloak を CI に含めるず起動に2分以䞊かかるため、認蚌のテストは JWT 怜蚌のモックで代替する

Phase 4: E2E テスト — Playwrightこのboilerplateには含たれたせん

Phase 4 はナヌザヌの実際の操䜜フロヌをブラりザを介しおテストするものです。蚭定コストが高いため、このboilerplateでは含めおいたせんが、重芁なナヌザヌフロヌを保護したい堎合に远加したす。

察象フロヌ最小限の掚奚セット:

テスト 内容 優先床
ログむンフロヌ トップペヌゞ → Keycloak → ダッシュボヌドぞのリダむレクト 高
アむテム䜜成 フォヌム入力 → 送信 → 䞀芧に衚瀺される 高
未認蚌リダむレクト /dashboard に盎接アクセス → トップペヌゞぞリダむレクト äž­
WebSocket 疎通 接続 → メッセヌゞ送信 → 受信衚瀺 䜎

実装する堎合の構成䟋:

e2e/
├── playwright.config.ts    # ベヌスURL・ブラりザ蚭定
├── fixtures/
│   └── auth.ts             # ログむン枈みペヌゞのフィクスチャ
└── tests/
    ├── auth.spec.ts        # ログむンフロヌ
    └── items.spec.ts       # アむテム CRUD

Keycloak ログむンの扱い:

Keycloak のログむンフォヌムを Playwright で操䜜するには page.fill() でナヌザヌ名・パスワヌドを入力する方法が䜿えたす。ただし以䞋の点に泚意が必芁です。

// e2e/fixtures/auth.ts
export async function loginAsTestUser(page: Page) {
  await page.goto('http://localhost')
  await page.click('button:has-text("Sign in")')
  // Keycloak のログむンフォヌム
  await page.fill('#username', 'normal-user')
  await page.fill('#password', 'password')
  await page.click('[type="submit"]')
  await page.waitForURL('**/dashboard/**')
}

怜蚎ポむント:

  • E2E はすべおのサヌビスを起動した状態docker compose upで実行するため CI 時間が倧幅に䌞びる。main ブランチぞのマヌゞ時のみ実行する蚭定が珟実的
  • Keycloak の起動に時間がかかるため、wait-on や docker compose wait で起動確認を挟む
  • テストデヌタは毎回 Keycloak の create-users.sh ず MongoDB コレクションの drop でリセットする
  • ブラりザの蚀語蚭定や画面サむズが Keycloak の UI に圱響するこずがあるplaywright.config.ts で固定する

䞻な機胜

nginx ルヌティング

nginx がシングル゚ントリヌポむントずしお党トラフィックを振り分けたす。

ブラりザ :80
    │
    ├─ /ws/               → backend:8000  (WebSocket)
    ├─ /api/sse/          → backend:8000  (SSE — バッファリング無効)
    ├─ /api/auth/         → frontend:3000 (NextAuth コヌルバック)
    ├─ /api/              → backend:8000  (FastAPI REST)
    ├─ /_next/static/     → frontend:3000 (immutable キャッシュ付き)
    ├─ /_next/webpack-hmr → frontend:3000 (開発甚 HMR WebSocket)
    └─ /                  → frontend:3000 (その他すべお)

/_next/static/ 配䞋のファむルはビルド時にコンテンツハッシュがファむル名に付䞎されるため、 nginx が Cache-Control: max-age=31536000, immutable を蚭定しおも安党です。

セキュリティヘッダヌChapter 4

nginx で以䞋のヘッダヌを党レスポンスに付䞎しおいたす。

ヘッダヌ 蚭定倀 効果
X-Frame-Options DENY クリックゞャッキング防止
X-Content-Type-Options nosniff MIME スニッフィング防止
Referrer-Policy strict-origin-when-cross-origin リファラヌ情報の制埡
Permissions-Policy カメラ等を無効化 䞍芁なブラりザ機胜を制限
Content-Security-Policy default-src 'self' ベヌス XSS・むンゞェクション察策

本番環境での泚意 CSP の unsafe-inline / unsafe-eval は Next.js の開発モヌドHMRに必芁ですが、 本番ビルドでは nonce ベヌスに倉曎しお削陀しおください。 Strict-Transport-Security は HTTPS 環境でのみ有効化しおください。

リトラむ凊理frontend/src/lib/fetch.ts

Chapter 4「Retry Strategies and Backoff」の実装です。党 API 呌び出しは玠の fetch の代わりに fetchWithRetry を䜿甚しおいたす。

ネットワヌク゚ラヌ・5xx → 指数バックオフでリトラむ最倧3回
4xx クラむアント゚ラヌ  → リトラむしないリク゚スト偎の問題

バックオフスケゞュヌルゞッタヌ付き

詊行 埅機時間
1回目倱敗 1000ms ± ランダムゞッタヌ
2回目倱敗 2000ms ± ランダムゞッタヌ
3回目倱敗 4000ms ± ランダムゞッタヌ

ゞッタヌを加えるこずで、耇数クラむアントが同時にリトラむした際にサヌバヌぞのリク゚ストが分散されたすthundering herd 問題の回避。

REST API (FastAPI)

/api/items/ に認蚌付きの CRUD ゚ンドポむントがありたす。

GET    /api/items/        ログむンナヌザヌのアむテム䞀芧
POST   /api/items/        アむテム䜜成 (201 Created)
GET    /api/items/{id}    アむテム取埗
DELETE /api/items/{id}    アむテム削陀 (204 No Content)
  • すべおの゚ンドポむントは Authorization: Bearer <token> を芁求したす。
  • Keycloak の JWKS ゚ンドポむントでトヌクンを怜蚌したす。
  • 各ナヌザヌは自分のアむテムのみ参照・操䜜できたす。

WebSocket (/ws/{client_id})

  • 接続した党クラむアントぞのブロヌドキャストチャットです。
  • JSON フォヌマット: { "message": "Hello" }
  • ブラりザを耇数タブで開いお動䜜を確認できたす。

SSE (/api/sse/tasks)

Server-Sent Events による䞀方向リアルタむム通信のサンプルです。

POST /api/sse/tasks          タスク開始Bearer 認蚌→ task_id を返す
GET  /api/sse/tasks/{task_id} EventSource で進捗むベントを賌読

EventSource は Authorization ヘッダヌを送れないため、 POST でタスクを開始しお task_id を取埗し、その task_id を URL に含めるこずで認蚌を代替しおいたすChapter 4 の SSE パタヌン準拠。

SSE WebSocket
通信方向 サヌバヌ→クラむアント䞀方向 双方向
向いおいる甚途 進捗通知・ログ・AI応答 チャット・ゲヌム
自動再接続 ブラりザ暙準で察応 実装が必芁

Keycloak SSO

  • keycloak/realm-export.json が起動時に自動むンポヌトされたす。
  • Next.js は next-auth + KeycloakProvider で認蚌したす。
  • FastAPI は Keycloak の JWKS を䜿っお Bearer トヌクンを怜蚌したす。

Keycloak URL の仕組みWSL2 Native Docker 察応

Docker 環境では Next.js コンテナずブラりザが異なるネットワヌクにいるため、 Keycloak ぞの接続に 2 皮類の URL を䜿い分けおいたす。

環境倉数 倀 甹途
KEYCLOAK_ISSUER http://localhost:8080/realms/myrealm トヌクンの iss 怜蚌・ブラりザ認蚌リダむレクト先
KEYCLOAK_INTERNAL_URL http://keycloak:8080/realms/myrealm サヌバヌサむドの API 呌び出しコンテナ内郚
KC_HOSTNAME_URL http://localhost:8080 Keycloak が生成する党 URL のベヌスiss クレヌムに反映

KC_HOSTNAME_URL を蚭定するこずで Keycloak が返すすべおの URLトヌクンの iss、 Discovery Document の各゚ンドポむントが localhost:8080 ベヌスになり、 ブラりザからの認蚌フロヌが正垞に動䜜したす。

認蚌チェックの二重構造

/dashboard 配䞋のペヌゞは、サヌバヌサむドずクラむアントサむドの䞡方でセッションを怜蚌しおいたす。

タむミング 実装箇所 察象ケヌス
ペヌゞ初回ロヌド時 dashboard/*/page.tsxサヌバヌコンポヌネントの getServerSession 未ログむンでの盎接アクセス
ペヌゞ衚瀺䞭のセッション倱効 NavBar.tsx の useEffectクラむアントコンポヌネント JWT 期限切れ・別タブからのログアりト
ペヌゞロヌド時
  └─ getServerSession → セッションなし → redirect('/')  サヌバヌサむド

ペヌゞ衚瀺䞭
  └─ useSession の status が 'unauthenticated' に倉化
       └─ pathname が /dashboard/* ならば router.push('/')  NavBar.tsx

なぜ NavBar に実装するのか: NavBar はすべおのペヌゞで描画されるクラむアントコンポヌネントで、すでに useSession ず usePathname を保持しおいたす。ここで䞀元管理するこずで、新しいデモペヌゞを远加しおも自動的に同じ保護が適甚されたす。

ナヌザヌをスクリプトで远加する

起動埌に Admin REST API 経由でナヌザヌを远加したい堎合:

docker compose exec keycloak bash /opt/keycloak/data/import/scripts/create-users.sh

keycloak/scripts/create-users.sh 内の create_user 行を远蚘するだけで 任意のナヌザヌを远加できたす。既存ナヌザヌはスキップされるため冪等に実行できたす。


本番環境に向けた TODO

docker-compose.prod.yml は本番構成の出発点ですが、公開前に以䞋を察応しおください。

  • NEXTAUTH_SECRET を匷力なランダム倀に倉曎するopenssl rand -base64 32
  • KEYCLOAK_ADMIN_PASSWORD を倉曎する
  • KEYCLOAK_CLIENT_SECRET を倉曎する
  • DB パスワヌドPOSTGRES_PASSWORDを倉曎する
  • KC_HOSTNAME_URL を本番ドメむンに倉曎する䟋: https://example.com
  • KEYCLOAK_ISSUER / NEXT_PUBLIC_API_URL を本番ドメむンに合わせる
  • nginx/nginx.conf に TLS 蚭定listen 443 ssl http2 + ssl_certificateを远加する
  • docker-compose.prod.yml で frontend・backend・keycloak の ports: を削陀し nginx のみ公開する
  • Keycloak の command: start-dev を start に倉曎する本番モヌド
  • Keycloak の sslRequired を "external" に倉曎する
  • 本番の MongoDB は認蚌付き URI を䜿甚する䟋: mongodb://user:pass@mongo:27017/appdb
  • 本番では MongoDB レプリカセット構成を怜蚎するマルチドキュメントトランザクション・読み取り敎合性
  • docker-compose.prod.yml に mem_limit / cpus でリ゜ヌス制限を远加する

O'Reilly Media, Inc. Fluent Web Developmentずの察応

ç«  本ボむラヌプレヌトぞの反映箇所
Chapter 2: Tooling Next.js 内蔵ビルドシステム採甚Vite/Webpack を個別管理しない
Chapter 3: Architecture C4モデルに基づいた nginx / Frontend / Backend / DB / Auth の分離
Chapter 4: Networking HTTP メ゜ッド・ステヌタスコヌドitems.py、WebSocketws.py、SSEsse.py、セキュリティヘッダヌnginx.conf、fetchWithRetryfetch.ts、nginx による immutable キャッシュ
Chapter 5: Rendering Server Component でのサヌバヌサむド認蚌チェックdashboard/*/page.tsx
Chapter 7: Robust CSS CSS レむダヌ・CSS 倉数によるベヌススタむルglobals.css、レむアりトずコンポヌネントの分離

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages