Source:
ocean/docs/adr/0013-consolidate-cloudflare-worker-to-supabase.md| ✏️ Edit on GitHub
ADR-0013: Consolidate Cloudflare Worker to Supabase Edge Functions
Status
Accepted
Context
The application had a Cloudflare Worker deployed separately to handle user creation webhooks from Supabase. When a new user signed up, the worker would:
- Receive the webhook from Supabase
- Create a Stripe customer
- Update the organization with the Stripe customer ID
This created unnecessary complexity:
- Multiple platforms: Frontend on Vercel, backend on Supabase, webhook handler on Cloudflare
- Separate deployment pipeline: Additional GitHub Actions workflow for Cloudflare
- Configuration overhead: Separate secrets management in Cloudflare
- Operational complexity: Another service to monitor and maintain
Meanwhile, we already had a similar Supabase Edge Function (sync-user-to-stripe) that performed almost the same functionality.
Decision
We will consolidate all webhook handling into Supabase Edge Functions and remove the Cloudflare Worker entirely.
Implementation
1. Enhanced Supabase Edge Function
Updated /supabase/functions/sync-user-to-stripe/index.ts to:
- Find the user's organization via
organization_memberstable - Create the Stripe customer with organization metadata
- Update the organization with the Stripe customer ID
- Handle edge cases where organization doesn't exist yet
2. Database Trigger
Created a database trigger that automatically calls the Edge Function when a user is created:
CREATE TRIGGER sync_user_to_stripe_on_insert
AFTER INSERT ON auth.users
FOR EACH ROW
EXECUTE FUNCTION public.sync_new_user_to_stripe();
The trigger function uses pg_net to make an HTTP request to the Edge Function, keeping everything within Supabase's infrastructure.
3. Removed Cloudflare Infrastructure
- Deleted
/cloudflare/worker/directory - Removed
.github/workflows/deploy-cloudflare-worker.yml - No longer need Cloudflare API tokens or deployment configuration
Architecture Before
┌─────────────┐ ┌─────────────┐ ┌──────────────┐
│ Vercel │ │ Supabase │────▶│ Cloudflare │
│ (Frontend) │ │ (Database) │ │ Worker │
└─────────────┘ └─────────────┘ └──────────────┘
│ │
│ ▼
│ ┌─────────────┐
└─────────────▶│ Stripe │
└─────────────┘
Architecture After
┌─────────────┐ ┌─────────────────────────────────┐
│ Vercel │ │ Supabase │
│ (Frontend) │────▶│ Database + Edge Functions │
└─────────────┘ │ + GraphQL + Auth │
└─────────────────────────────────┘
│
▼
┌─────────────┐
│ Stripe │
└─────────────┘
Consequences
Positive
- Simplified stack: Only two platforms (Vercel + Supabase) instead of three
- Unified backend: All backend logic in one place
- Simplified deployment: One less CI/CD pipeline to maintain
- Consistent security: All backend services use Supabase's security model
- Easier debugging: All logs and monitoring in Supabase dashboard
- Cost efficiency: No additional Cloudflare Workers charges
- Single source of truth: All configuration in Supabase environment
Negative
- Vendor lock-in: More dependent on Supabase's ecosystem
- Migration effort: Need to update any documentation or runbooks
- Trigger complexity: Database triggers are harder to debug than webhooks
Neutral
- Performance: Both solutions have similar latency characteristics
- Reliability: Both Cloudflare and Supabase have excellent uptime
- Scalability: Both can handle high webhook volumes
Migration Notes
- The database trigger is created in migration
20250729_create_user_sync_webhook.sql - Existing users already have Stripe customers, so this only affects new signups
- The
retry_user_stripe_syncfunction allows manual retries if needed - Monitor
net.http_request_queuetable for webhook delivery status
Security Considerations
- Authentication: Uses Supabase service role key from vault
- Network: All communication stays within Supabase's network
- Logging: Webhook requests are logged for audit trail
- Error handling: Failed webhooks can be retried manually
Alternatives Considered
1. Keep Cloudflare Worker
- Rejected: Adds unnecessary complexity for minimal benefit
2. Direct Database-to-Stripe Integration
- Rejected: Would require storing Stripe keys in database
3. Queue-based System
- Rejected: Over-engineering for simple webhook needs
References
Related ADRs
- ADR-0001: Adopt TanStack Ecosystem
- ADR-0012: GraphQL-Based Billing Architecture