概要
Sprint Cycle 18では、Supabase依存からの脱却を完遂しました。Fly Postgres(PostgreSQL 17.7)をNRTリージョンにセットアップし、auth.usersやRLSポリシーを除去して自前のusersテーブルを作成。JWT自前発行とbcryptパスワード認証を実装し、signup/loginエンドポイントを新設しました。ApiError enumの拡張と3つの認証テストを追加し、全18テスト合格。最終的にAPI本番環境をmiseban-ai-api.fly.devにデプロイ完了しました。
| 対応内容 | 状態 |
|---|---|
| Fly Postgres (PostgreSQL 17.7) NRTリージョンセットアップ | 完了 |
| 独自スキーマ移行: auth.users/RLS除去、自前usersテーブル作成 | 完了 |
| JWT自前発行 + bcryptパスワード認証 (signup/login) | 完了 |
| ApiError enum拡張 + 認証テスト3件追加 (18テスト全合格) | 完了 |
| API本番デプロイ: miseban-ai-api.fly.dev | 完了 |
1. Fly Postgres (PostgreSQL 17.7) NRTリージョンセットアップ
これまでSupabaseのマネージドPostgreSQLに依存していましたが、今回のスプリントでFly.io上に自前のPostgresクラスタを構築しました。
- PostgreSQL 17.7: 最新の安定バージョンを採用
- NRTリージョン: 東京リージョンに配置し、APIサーバーと同一リージョンでレイテンシを最小化
- Supabase依存の脱却: 外部サービスへの依存を排除し、インフラ全体をFly.ioに統一
Fly Postgresはアプリと同一プラットフォーム上で動作するため、ネットワークホップが最小限に抑えられます。これにより、DB接続のレイテンシが大幅に改善されました。また、Supabaseの無料枠制限やRLSの複雑さから解放され、シンプルなアーキテクチャを実現しています。
2. 独自スキーマ移行
Supabase固有のauth.usersテーブルやRLS(Row Level Security)ポリシーを完全に除去し、自前のスキーマに移行しました。
-- Supabase依存を除去した新しいusersテーブル
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
store_name TEXT,
tier TEXT NOT NULL DEFAULT 'free',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Supabaseのauth.usersスキーマは多くの隠れた依存関係(トリガー、関数、RLSポリシー)を持っており、デバッグやマイグレーションの障害になっていました。自前テーブルに移行したことで、スキーマの全体像が明確になり、マイグレーションの管理も容易になっています。
RLSポリシーの除去
Supabase環境ではRLSポリシーによるアクセス制御を行っていましたが、APIサーバーがDB接続を一元管理する構成では不要です。RLSを除去し、アクセス制御はAPIレイヤーのミドルウェアで一括管理する方針に切り替えました。これにより「RLSが204を返すが実際には書き込まれていない」というサイレント失敗の問題も根本的に解消されます。
3. JWT自前発行 + bcryptパスワード認証
Supabase Authに代わる認証基盤として、JWT自前発行とbcryptパスワードハッシュを実装しました。
// POST /api/v1/auth/signup
async fn signup(Json(req): Json<SignupRequest>) -> Result<Json<AuthResponse>, ApiError> {
let password_hash = bcrypt::hash(&req.password, 12)?;
let user = sqlx::query_as!(User, /* INSERT INTO users ... */)
.fetch_one(&pool).await?;
let token = encode_jwt(&user.id, &jwt_secret)?;
Ok(Json(AuthResponse { token, user }))
}
// POST /api/v1/auth/login
async fn login(Json(req): Json<LoginRequest>) -> Result<Json<AuthResponse>, ApiError> {
let user = find_user_by_email(&req.email).await?;
bcrypt::verify(&req.password, &user.password_hash)?;
let token = encode_jwt(&user.id, &jwt_secret)?;
Ok(Json(AuthResponse { token, user }))
}
- bcrypt (cost=12): パスワードハッシュに業界標準のbcryptを採用。コストファクター12で十分なセキュリティを確保
- JWT発行: ログイン成功時にJWTトークンを発行。既存の認証ミドルウェアとシームレスに統合
- signup/loginエンドポイント:
/api/v1/auth/signupと/api/v1/auth/loginの2つのエンドポイントを新設
Supabase Authへの外部HTTPコールが不要になったため、認証フローのレイテンシが改善されました。また、トークンの有効期限やクレーム構造を完全に制御できるようになっています。
4. ApiError enum拡張 + 認証テスト追加
認証機能の実装に伴い、エラーハンドリングを強化しました。
#[derive(Debug)]
enum ApiError {
Internal(String),
NotFound(String),
Unauthorized(String), // NEW
BadRequest(String), // NEW
// ...
}
新規テスト3件
認証フローをカバーする3つのテストを追加し、テスト合計を18件に引き上げました。
- signup_creates_user_and_returns_token: signupエンドポイントがユーザーを作成し、有効なJWTトークンを返すことを検証
- login_with_valid_credentials_returns_token: 正しいメールアドレスとパスワードでログインした場合にトークンが返ることを検証
- login_with_invalid_password_returns_401: 不正なパスワードでログインした場合に
401 Unauthorizedを返すことを検証
これらのテストにより、認証フローの正常系と異常系の両方がカバーされています。既存の15テストへの影響はなく、全18テストが合格しています。
5. API本番デプロイ
全ての変更をまとめてmiseban-ai-api.fly.devに本番デプロイを完了しました。
- Fly Postgres接続: 内部ネットワーク経由でDB接続(
.flycastアドレス) - 環境変数:
DATABASE_URL、JWT_SECRETをFly.ioシークレットとして設定 - ヘルスチェック:
/healthエンドポイントでDB接続状態を含めた稼働確認
APIサーバー、データベース、ランディングページが全てFly.ioプラットフォーム上に統一されたことで、運用管理がシンプルになりました。
テスト結果
全18テスト合格、cargo check警告ゼロ。既存テストへの影響はありません。
| 指標 | Before | After |
|---|---|---|
| テスト合格数 | 15/15 | 18/18 |
| cargo check警告 | 0 | 0 |
| 新規エンドポイント | - | 2件(signup/login) |
| DB基盤 | Supabase PostgreSQL | Fly Postgres (PG 17.7) |
| 認証基盤 | Supabase Auth | 自前JWT + bcrypt |
| 本番URL | - | miseban-ai-api.fly.dev |
Supabase依存を完全に脱却し、Fly Postgres + 自前認証による自立したインフラ構成を実現しました。DB、認証、APIサーバーの全てがFly.ioプラットフォーム上に統一され、運用の簡素化とレイテンシ改善を達成。18テスト全合格で品質を担保しつつ、本番デプロイも完了しています。