導入
朝からeurekapu-nuxt4の認証・DB・デプロイ周りに手を入れ続けて、気づいたらターミナルのログが画面から溢れていた。SSRでのクッキー転送、D1マイグレーション、画像配信ルート上限、Google OAuth、E2Eテスト -- 一つ直すと次の問題が顔を出す連鎖を一日がかりで潰していった。
SSRでのクッキー転送問題
SSR環境でサーバーサイドからAPIを叩くと、ブラウザのクッキーが転送されず認証が通らなかった。fetchAccessInfoでheadersを明示的に渡すように修正。SSRではブラウザ→Nuxtサーバー→APIの2段階になるので、最初のリクエストのヘッダーを手動で引き回す必要がある。
// useRequestHeaders()でブラウザのCookieを取得し、fetch時に渡す
const headers = useRequestHeaders(['cookie'])
const data = await fetchAccessInfo({ headers })
Better Auth: メール+パスワード認証の追加
開発環境でのテスト用に、メール+パスワード認証を追加した。import.meta.devで分岐させて、本番環境ではGoogle OAuthのみに絞っている。
管理者メール(ADMIN_EMAILS)も本番と開発で分離。開発環境では[email protected]系のアドレスを管理者として扱う。
D1マイグレーション管理
0003から0005まで3本のマイグレーションを作成し、ローカルとリモートの両方に適用した。
外部キーのON DELETE CASCADE統一(0005)
SQLiteではALTER TABLEで外部キー制約を変更できないため、テーブルを作り直す必要がある。0005マイグレーションでは以下の手順を踏んだ:
- 新テーブルを作成(CASCADE付き)
- データをコピー
- 旧テーブルを削除
- リネーム
リモートD1からは本番で不要なpasswordカラムも削除。スキーマスナップショットをドキュメントとして残し、ローカルとリモートの差分を比較できるようにした。
quiz_answerのquestion_idフォーマット不一致
quiz_answerテーブルのquestion_idが、あるパスではハイフン区切り、別のパスではスラッシュ区切りで保存されていた。テストを書いて不一致を再現させてから、フォーマットを統一するように修正した。
画像配信: _routes.jsonの100ルール上限
Cloudflare Pagesの_routes.jsonにはルールが100件までという上限がある。画像パスを個別に列挙していたら上限に引っかかった。ワイルドカード化して対処。
Google OAuthのredirect_uri_mismatch
デプロイ後にGoogle OAuthでredirect_uri_mismatchエラーが発生。BETTER_AUTH_URL環境変数が正しく設定されていなかった。合わせてTRUSTED_ORIGINSの正規化処理をURL.originを使う形に修正し、末尾スラッシュの有無で弾かれる問題を潰した。
UIの微調整
- ヘッダーをstickyレイアウトに修正。スクロールしてもナビゲーションが追従するようにした
- ヒーローテキストのフォントサイズ調整
E2Eテスト強化
CIのE2Eテストが不安定だったので、Chromiumのみ実行に変更。--project引数の渡し方も修正した。
テストカバレッジは55件から78件に増加。新しく追加した認証フローやクイズ回答のテストが大半を占める。
その他の修正
- ログアウト後のリダイレクト先を
/loginから/に変更 .env.exampleからVPS IPを削除(公開リポジトリに残すべきでない情報)
振り返り
D1マイグレーションでは、SQLiteの「外部キーを後から変更できない」制約に3回テーブル再作成を繰り返した。RDBMSごとのALTER TABLEの差異を体で覚えた一日だった。
_routes.jsonの100件上限は、ドキュメントを読み飛ばしていて踏んだ地雷。Cloudflare Pagesの制約はビルドが通ってからデプロイ後に判明するパターンが多いので、制約一覧を手元にまとめておく価値がある。
テスト数が78件まで積み上がったことで、認証フロー周りの変更を入れるときの安心感が変わった。壊れたらCIが教えてくれるという状態を一日で作れたのは収穫。