Cloudflare Workers + Supabase 環境でのDB関連技術選定の変遷

要約
Cloudflare Workers+Supabase環境でのDB技術選定の奮闘記。Supabaseオールイン戦略から始まり、スキーマ管理やトランザクションの壁に直面。PrismaやDrizzle導入の試行錯誤でツールは乱立。その変遷と理想構成への道を解説します。
意見はこのエリアに表示されます

この記事の目的

Cloudflare WorkersとSupabaseを採用する際に、特にデータベース関連の技術選定(ORM、マイグレーション、スキーマ管理など)で迷った経験はありませんか?本記事では、筆者が実際に直面した課題と試行錯誤の過程を赤裸々に綴ります。
この記事が、同じような構成で開発を進める方々にとって、未来の自分からの「欲しかった情報」となることを目指します。


目次

  1. この記事の目的
  2. 技術選定の要素と構造
  3. 固定条件
  4. 技術選定の変遷
  5. 今後の構想
  6. もし今から始めるなら:Neon DBで構築する理想構成
  7. まとめ: 技術選定の教訓

技術選定の要素と構造

技術選定の主要要素

現代のWebアプリケーション開発では、以下の要素が相互に影響し合いながら技術スタックを形成します:

本プロジェクトで検討した要素

  • Client: データベース接続方式(TCP vs HTTP, プロトコル選択)
  • ORM: オブジェクト関係マッピング(型安全性、開発体験)
  • Migration: スキーマ変更管理(バージョニング、チーム協調)
  • Schema管理: スキーマ定義の真実の源泉(Code-first vs DB-first)
  • Seed: 初期データ・テストデータ管理
  • Transaction: トランザクション制御方式(App Layer vs DB Layer)

固定条件

Cloudflare Workers

  • 理由: 第一線で活躍するエンジニアからの推薦による。エッジコンピューティングが実現するパフォーマンスと、極めて快適な開発体験に魅力を感じたため。

Supabase (PostgreSQL)

  • 理由: 当時の技術トレンドであったことに加え、MySQLからPostgreSQLへ移行したいという個人的な関心があったため。

技術選定の変遷

Phase 1: Supabase オールイン戦略

初期構想: Supabase ですべてを解決

要素選択理由
Client@supabase/supabase-jsプラットフォーム統合
ORMなし(生API)Supabase API で十分
MigrationSupabase CLISupabaseオール・イン
Schema管理Supabase CLISupabaseオール・イン
Seedなし初期段階では不要
Transactionなし特別な技術が必要だとは想定していなかった

📝 振り返り・学び

単純に技術選定のための知識、特にトランザクションに関する理解が不足していた。


Phase 2: 宣言的スキーマ管理の必要性

💭 当時の状況・思考

この時点では、宣言的にスキーマを管理するという手法を知らなかった。
また、ベンダーロックインを懸念し、DBを切り離して汎用的に管理したいという意図があった。

※ 宣言的スキーマ管理とは
宣言的: コードでスキーマの「あるべき姿」を定義し、ツールが現状との差分を自動計算・適用
Migration管理: 変更を順次SQLファイルで記録し、履歴順に適用
例: Prisma schema.prisma → 自動でALTER文生成 vs 手動で 001_add_users.sql 作成

※ ベンダーロックインとは
特定のクラウドサービスに依存しすぎて、他のサービスへの移行が困難になること
例: Supabaseダッシュボードでスキーマ管理 → 移行時にSupabase固有設定の再構築が必要

❌ 問題発生

ベンダーロックインとスキーマ管理

✅ 解決策・決断

Prisma でスキーマ管理

要素変更前変更後理由
Schema管理Supabase CLIPrisma Schema宣言的管理、VC対応
ORM生API検討中型安全性向上

📝 振り返り・学び

宣言的管理という概念は以前から漠然と認識していたが、この時初めて深く調査し、その後の開発に大きな影響を与えた。この宣言的な考え方から発想して、Seedデータも決定論的に管理するアプローチを採用した。

※ 決定論的Seedデータ管理
UUIDを決定論的に生成 → 何度実行しても同一データ
例: user_id: uuid("admin-user") → 常に同じUUIDが生成され、全環境で一致


Phase 3: 権限管理とマイグレーションの複雑化

❌ 問題発生

Prisma では DB 権限やfunctionを宣言的に管理できない

Prismaでpushしたスキーマに対し、APIからアクセスを試みたところ、権限不足でテーブルにアクセスできない問題が発生した。Prismaのスキーマ定義はテーブル構造のみに特化しており、データベース権限(GRANT/REVOKE)やPostgreSQL関数を宣言的に管理することができない※。やむを得ず、Supabase CLIによるマイグレーションを導入するに至った。

※SupabaseのAPI経由でアクセスする場合、public schemaへのGRANT権限が必要だった

✅ 解決策・決断

Supabase CLI でマイグレーション導入

この時点でスキーマ変更時に実行するreset-dbコマンドはかなり複雑化した。この状況を改善したいと考えつつも、当時は開発の進行を優先した。

要素Phase2Phase3理由
MigrationPrisma MigrateSupabase CLI権限管理対応
Schema管理Prisma SchemaPrisma + SQLハイブリッド管理

📝 振り返り・学び

プロトタイプの開発を優先するあまり、この時点での技術選定を深く検討せず、力技で開発を進めてしまった。最終的にこの構成は変更することになったが、時には実装を優先すべき局面もあり、まさにここがそのタイミングだったと今では考えている。


第4フェーズ:トランザクション制御の限界

当時の状況・思考

購入関連機能の実装を進める中で、Supabaseのクライアントライブラリでは、複数のDB操作を一つのトランザクションとしてまとめられないという制約に直面した。

SupabaseのクライアントはHTTPベースで動作するため、以下のように記述したコードはそれぞれが独立したリクエストとして実行される。そのため、途中の処理でエラーが発生しても、それ以前の操作をロールバック(取り消し)できない。

問題発生

HTTPベースでアプリケーション層トランザクション不可

Supabase公式の推奨策とその課題

Supabaseでは、このような一連の処理を**DB Function(ストアドプロシージャ)としてデータベース層に定義し、それをクライアントから呼び出す方法を推奨している。この方法であれば、一連の処理の原子性(アトミック性)**が保証される。

※ アトミック性
関連する一連の処理が**「すべて成功」するか「すべて失敗(実行前の状態に戻る)」するかのどちらか**であることが保証される性質。

しかし、このアプローチを試みた結果、新たな課題が浮上した。

  • 複雑化するロジック管理
    • 複雑なビジネスロジックをSQLで記述する必要があり、可読性やメンテナンス性が低下する。
    • デバッグが困難になる。
  • 開発プロセスの非効率化
    • 機能が増えるたびにDB Functionが増加し、マイグレーションファイルでのバージョン管理が非常に煩雑になる。

試行錯誤・調査

当初はSupabaseの思想に沿って開発を進めていたが、DB Functionの管理コストが現実的ではないと判断し、他の方法を探し始めた。宣言的に管理できるツールとして「Atlas」も候補に挙がったが、npmパッケージではなかったため採用には至らなかった。

振り返りと学び

今回の経験から、SupabaseのDB Functionによるトランザクション管理は、以下のようなケースでは有効な選択肢だと考えられる。

  • 小規模なアプリケーションで、トランザクション処理が少ない場合
  • 処理ロジックが単純で、変更頻度が低い場合

一方で、複雑なビジネスロジックや頻繁な仕様変更が求められるプロジェクトでは、DB Functionに依存しすぎると管理コストが膨らむ可能性がある。プロジェクトの特性に応じて、アーキテクチャを慎重に選択することの重要性を学んだ。


Phase 5: Client とORMの選択

💭 当時の状況・思考

この段階に至り、初めてClientとORMの選定に本格的に着手した。

🔍 試行錯誤・調査

検討した選択肢:

  1. Prisma Client: 学習コストがなく理想的だが、Cloudflare Workersに非対応。クエリも独特。
  2. Drizzle ORM: Workersに対応しており、型安全性も高い。クエリがSQLに近く、馴染みやすかった。

❌ Prisma Client の制約

✅ Drizzle ORM の採用

要素Phase3Phase4理由
ClientSupabase Clientpostgres.jsTCP直接接続、Workers対応
ORMなしDrizzle型安全性、Workers対応
TransactionHTTP制限App Layer柔軟な制御

Phase 6: ツール乱立による複雑化と、理想への葛藤(現在)

ツール乱立による複雑化:

  • Prisma: スキーマ定義・開発時DB管理、Seedデータ挿入
  • Supabase CLI: マイグレーション・権限管理
  • Drizzle: 本番ORM・型生成
  • postgres.js: 接続クライアント

現在の開発フロー例:(コマンドで統一済)

理想: Drizzle 統一

❌ 現実的な制約: Prisma の開発体験が良すぎる

Prismaによるスキーマ管理の体験は極めて優れている。schema.prismaはコード量が少なく直感的であり、AI(LLM)にコンテキストとして与える際のトークン量削減にも寄与する。開発期間中はこの利点を最大限に活用したいという思いがあった。

現在の妥協案(Phase 6)

schemaの極端な変更がありえるうちはschema.prismaで管理

要素現在の仕組み
Schema管理Prisma Schema
MigrationSupabase CLI
ORMDrizzle (DB introspect)
Clientpostgres.js
SeedPrisma Script

今後の構想

本番運用に向けた移行計画

  1. Phase 7: DB Functionをアプリケーションコードに移行
  2. Phase 8: Prisma Schema → Drizzle Schema 変換
  3. Phase 9: Drizzle Kit での完全なマイグレーション管理

各要素の移行予定

要素移行予定
Schema管理Drizzle Schema
MigrationDrizzle Kit
ORMDrizzle
Clientpostgres.js
SeedDrizzle Script

もし今から始めるなら:Neon DBで構築する理想構成

※これはあくまで仮定の話で実際にneonを使用していない自分の想像です

理想的な技術選定プロセス

なぜNeon DBか:

Neon DBを選択する最大の理由は、現状のプロジェクトでSupabaseの多機能性を活かしきれていない点にある。Storage、Supabase CLI、Edge Functionは使用していないし、Authは利用しているものの、他のツールで十二分に代替可能であるように思う。一方、Neonが提供する無料のDBブランチ機能やサーバーレス環境での自動スケーリングは、本プロジェクトにとって非常に魅力的に見える。

段階的な理想構成:

開発期間中(本番運用まで):

注意: 開発期間中はdb-reset前提のため、Prisma push後の他ツールマイグレーション実行は問題なし。履歴管理不要で任意のツール組み合わせが可能。

本番運用開始時:

要素開発期間中本番運用時理由
DBNeon DBNeon DBServerless最適化、ブランチング
Client@neondatabase/serverless@neondatabase/serverlessHTTP、Workers完全対応
ORMDrizzleDrizzle軽量、型安全、Workers対応
MigrationPrisma MigrateDrizzle Kit開発効率 → 一貫性
SchemaPrisma Schema → introspectDrizzle SchemaAI開発用 → 実行用
SeedPrisma ScriptDrizzle Script開発効率 → 環境一貫性

移行戦略の利点:

  • 開発期間: Prismaの優れた開発体験を活用
  • 本番運用: Drizzleの統一性とパフォーマンスを享受
  • 段階的移行: リスクを最小化しながら最適化

Schema管理のハイブリッド戦略:

AI適正の観点:

  • Prisma Schema: 1ファイルで全体構造把握、AI開発時の参照用
  • 最高の開発体験: 高速scrap&build + AI可読性の両立

まとめ: 技術選定の教訓

重要な原則

  1. 基盤システムから設計する: トランザクション制御のような基盤技術は、後から変更すると全体のアーキテクチャに大きな影響を与える。UI開発に入る前に、データ操作の基本パターンを明確にし、それに適したツール選定を行う。

  2. 最低限の移行余地を残す: DB層の分離(例:Drizzle接続の抽象化)など、過度ではない最小限の設計で将来の変更に対応する。

  3. 複数ツールの試行錯誤を恐れない: 様々なツールを試すことで各ツールの特性や制約を深く理解でき、最終的により適切な選択と効率的な実装につながる。

  4. フェーズに応じた最適化: 開発期間中は開発効率を、本番運用時はパフォーマンスと一貫性を重視するなど、プロジェクトのフェーズに応じて優先度を調整する。

避けるべき落とし穴

  • 最初から完璧を求める: 要件が変化する前提で柔軟な設計を心がける
  • 単一ツール至上主義: ツールの制約に振り回されるより、要件に最適な組み合わせを選ぶ
  • ツール統一の強制: 開発フェーズを無視した一貫性の追求は非効率

この変遷の記録が、Cloudflare WorkersとサーバーレスDBという同様の環境で開発を進める方々の参考となれば幸いです。

Explore More
関連記事はありません。
Trends