DocHub
Complete database and auth migration from Firebase/Firestore to Supabase/Postgres

Firebase to Supabase Migration

Overview

Full migration of Ricoya from Firebase (Firestore + Firebase Auth + Firebase Storage) to Supabase (Postgres + Supabase Auth + Cloudflare R2). Executed across multiple sessions in February 2026.

Schema Design

Created 22 Postgres tables replacing 18 Firestore collections:

Table Purpose
config App configuration key-value pairs
tax_rates Tax rate definitions
users User profiles with auth_uid reference
restaurants Restaurant info, hours, images
restaurant_categories Menu categories per restaurant
restaurant_category_tax_mappings Tax-to-category associations
menu_items Food items with pricing and images
menu_customization_groups Add-on groups for menu items
addresses Customer delivery addresses
discount_codes Promo codes with usage limits
orders Order records with status tracking
order_items Individual items within orders
discount_code_logs Discount code usage history
assignments Staff-to-restaurant assignments
restaurant_logs Restaurant activity audit trail
file_uploads Uploaded file metadata
ratings Customer ratings for orders
_id_map Firebase-to-Supabase ID mapping
push_subscriptions Web push notification endpoints
restaurant_sales_view SQL VIEW replacing pre-computed reports

Supporting infrastructure:

  • 7 enums for order status, payment method, user role, etc.
  • 24 indexes for query performance
  • 4 PL/pgSQL functions for atomic operations (order creation with discount, driver claim, credit grant/redeem)
  • RLS policies on all tables

Auth Migration

Migrated 79 users from Firebase Auth to Supabase Auth:

  • Google OAuth users and phone OTP users created via Admin API
  • Random passwords assigned (users re-authenticate via Google/Phone)
  • Firebase UID stored in user metadata for reference
  • 2 test accounts skipped
  • ID mapping file generated for Firebase UID to Supabase UUID lookup

Data Migration

764 rows migrated across 17 tables:

  • config: 5, tax_rates: 2, users: 79, restaurants: 10
  • restaurant_categories: 61, category_tax_mappings: 7
  • menu_items: 313, customization_groups: 34
  • addresses: 49, discount_codes: 6
  • orders: 45, order_items: 62, discount_code_logs: 7
  • assignments: 10, restaurant_logs: 48, file_uploads: 26
  • _id_map: 592 (ID mapping records)
  • 31 records skipped (test data, orphaned references, deleted codes)

Repository Layer Rewrite

17 repository files rewritten from Firestore SDK to Supabase client:

  • All repos now use Supabase query builder instead of Firestore collection queries
  • Dual-ID problem eliminated with single Postgres SERIAL primary keys
  • Firestore transactions replaced with RPC calls to PL/pgSQL functions
  • Server-side repos use admin client (service role key)

API Route Rewrite

Approximately 50 API route files rewritten using 4 parallel agents:

  • 10 admin routes (addresses, assignments, categories, discount-codes, drivers, orders, order-items)
  • 11 admin routes (reports, restaurants, taxes, users)
  • 18 routes (driver, payment, order-detail, cart, public, push)
  • 3 services + login page + profile page + ManagerConsole + 3 scripts

New route created for Supabase OAuth redirect handling.

Client Library Changes

  • Browser client created via @supabase/ssr
  • Server client created with service role key
  • Server auth changed from Firebase verifyIdToken to Supabase auth.getUser
  • AuthContext changed from Google popup to OAuth redirect, Phone reCAPTCHA to OTP
  • API client changed from Firebase getIdToken to Supabase getSession

Build Fixes

After migration, 10+ files had Firebase-specific type references:

  • user.uid changed to user.id (Supabase User type)
  • getIdToken() replaced with getSession()
  • phoneNumber changed to phone
  • displayName changed to user_metadata.display_name
  • Restaurant model updated with numeric id, legacy_firestore_id, legacy_numeric_id

Legacy Compatibility

The codebase uses firestore_id as a compatibility layer:

  • Restaurant URLs use legacy Firebase document IDs
  • 4 repositories resolve legacy_firestore_id to numeric Supabase ID before querying
  • restaurantsRepo maps legacy_firestore_id (or String(id) fallback) to firestore_id field