@intentsolutionsio/supabase-pack 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +73 -47
- package/package.json +4 -4
- package/skills/supabase-advanced-troubleshooting/SKILL.md +404 -200
- package/skills/supabase-advanced-troubleshooting/references/errors.md +11 -0
- package/skills/supabase-advanced-troubleshooting/references/evidence-collection-framework.md +34 -0
- package/skills/supabase-advanced-troubleshooting/references/examples.md +11 -0
- package/skills/supabase-advanced-troubleshooting/references/rls-edge-functions-realtime.md +363 -0
- package/skills/supabase-advanced-troubleshooting/references/systematic-isolation.md +56 -0
- package/skills/supabase-advanced-troubleshooting/references/timing-analysis.md +35 -0
- package/skills/supabase-architecture-variants/SKILL.md +395 -216
- package/skills/supabase-architecture-variants/references/errors.md +11 -0
- package/skills/supabase-architecture-variants/references/examples.md +12 -0
- package/skills/supabase-architecture-variants/references/serverless-and-multi-tenant.md +251 -0
- package/skills/supabase-architecture-variants/references/variant-a-monolith-(simple).md +44 -0
- package/skills/supabase-architecture-variants/references/variant-b-service-layer-(moderate).md +72 -0
- package/skills/supabase-architecture-variants/references/variant-c-microservice-(complex).md +81 -0
- package/skills/supabase-auth-storage-realtime-core/SKILL.md +471 -37
- package/skills/supabase-ci-integration/SKILL.md +315 -67
- package/skills/supabase-ci-integration/references/errors.md +10 -0
- package/skills/supabase-ci-integration/references/examples.md +36 -0
- package/skills/supabase-ci-integration/references/implementation.md +54 -0
- package/skills/supabase-common-errors/SKILL.md +320 -62
- package/skills/supabase-common-errors/references/errors.md +53 -0
- package/skills/supabase-common-errors/references/examples.md +23 -0
- package/skills/supabase-cost-tuning/SKILL.md +365 -131
- package/skills/supabase-cost-tuning/references/cost-estimation.md +34 -0
- package/skills/supabase-cost-tuning/references/cost-reduction-strategies.md +40 -0
- package/skills/supabase-cost-tuning/references/errors.md +11 -0
- package/skills/supabase-cost-tuning/references/examples.md +15 -0
- package/skills/supabase-data-handling/SKILL.md +378 -145
- package/skills/supabase-data-handling/references/errors.md +11 -0
- package/skills/supabase-data-handling/references/examples.md +27 -0
- package/skills/supabase-data-handling/references/implementation.md +223 -0
- package/skills/supabase-data-handling/references/retention-and-backup.md +221 -0
- package/skills/supabase-debug-bundle/SKILL.md +267 -73
- package/skills/supabase-debug-bundle/references/errors.md +12 -0
- package/skills/supabase-debug-bundle/references/examples.md +24 -0
- package/skills/supabase-debug-bundle/references/implementation.md +54 -0
- package/skills/supabase-deploy-integration/SKILL.md +258 -147
- package/skills/supabase-deploy-integration/references/errors.md +11 -0
- package/skills/supabase-deploy-integration/references/examples.md +21 -0
- package/skills/supabase-deploy-integration/references/google-cloud-run.md +36 -0
- package/skills/supabase-deploy-integration/references/vercel-deployment.md +35 -0
- package/skills/supabase-enterprise-rbac/SKILL.md +327 -160
- package/skills/supabase-enterprise-rbac/references/api-scoping-and-enforcement.md +255 -0
- package/skills/supabase-enterprise-rbac/references/errors.md +11 -0
- package/skills/supabase-enterprise-rbac/references/examples.md +12 -0
- package/skills/supabase-enterprise-rbac/references/role-implementation.md +33 -0
- package/skills/supabase-enterprise-rbac/references/sso-integration.md +35 -0
- package/skills/supabase-hello-world/SKILL.md +160 -54
- package/skills/supabase-incident-runbook/SKILL.md +453 -131
- package/skills/supabase-incident-runbook/references/errors.md +11 -0
- package/skills/supabase-incident-runbook/references/examples.md +10 -0
- package/skills/supabase-incident-runbook/references/immediate-actions-by-error-type.md +41 -0
- package/skills/supabase-install-auth/SKILL.md +186 -50
- package/skills/supabase-install-auth/references/examples.md +102 -0
- package/skills/supabase-known-pitfalls/SKILL.md +411 -241
- package/skills/supabase-known-pitfalls/references/errors.md +11 -0
- package/skills/supabase-known-pitfalls/references/examples.md +12 -0
- package/skills/supabase-load-scale/SKILL.md +346 -217
- package/skills/supabase-load-scale/references/capacity-planning.md +47 -0
- package/skills/supabase-load-scale/references/errors.md +11 -0
- package/skills/supabase-load-scale/references/examples.md +26 -0
- package/skills/supabase-load-scale/references/load-testing-with-k6.md +59 -0
- package/skills/supabase-load-scale/references/scaling-patterns.md +65 -0
- package/skills/supabase-load-scale/references/table-partitioning.md +263 -0
- package/skills/supabase-local-dev-loop/SKILL.md +272 -73
- package/skills/supabase-local-dev-loop/references/errors.md +11 -0
- package/skills/supabase-local-dev-loop/references/examples.md +21 -0
- package/skills/supabase-local-dev-loop/references/implementation.md +60 -0
- package/skills/supabase-migration-deep-dive/SKILL.md +338 -177
- package/skills/supabase-migration-deep-dive/references/backfill-versioning-rollback.md +258 -0
- package/skills/supabase-migration-deep-dive/references/errors.md +11 -0
- package/skills/supabase-migration-deep-dive/references/examples.md +12 -0
- package/skills/supabase-migration-deep-dive/references/implementation-plan.md +80 -0
- package/skills/supabase-migration-deep-dive/references/pre-migration-assessment.md +39 -0
- package/skills/supabase-multi-env-setup/SKILL.md +393 -152
- package/skills/supabase-multi-env-setup/references/configuration-structure.md +59 -0
- package/skills/supabase-multi-env-setup/references/errors.md +11 -0
- package/skills/supabase-multi-env-setup/references/examples.md +11 -0
- package/skills/supabase-observability/SKILL.md +318 -196
- package/skills/supabase-observability/references/alert-configuration.md +40 -0
- package/skills/supabase-observability/references/errors.md +11 -0
- package/skills/supabase-observability/references/examples.md +13 -0
- package/skills/supabase-observability/references/metrics-collection.md +65 -0
- package/skills/supabase-performance-tuning/SKILL.md +304 -160
- package/skills/supabase-performance-tuning/references/caching-strategy.md +49 -0
- package/skills/supabase-performance-tuning/references/errors.md +11 -0
- package/skills/supabase-performance-tuning/references/examples.md +13 -0
- package/skills/supabase-policy-guardrails/SKILL.md +248 -221
- package/skills/supabase-policy-guardrails/references/ci-cost-security.md +484 -0
- package/skills/supabase-policy-guardrails/references/errors.md +11 -0
- package/skills/supabase-policy-guardrails/references/eslint-rules.md +46 -0
- package/skills/supabase-policy-guardrails/references/examples.md +10 -0
- package/skills/supabase-prod-checklist/SKILL.md +474 -84
- package/skills/supabase-prod-checklist/references/errors.md +63 -0
- package/skills/supabase-prod-checklist/references/examples.md +153 -0
- package/skills/supabase-prod-checklist/references/implementation.md +113 -0
- package/skills/supabase-rate-limits/SKILL.md +311 -98
- package/skills/supabase-rate-limits/references/errors.md +11 -0
- package/skills/supabase-rate-limits/references/examples.md +46 -0
- package/skills/supabase-rate-limits/references/implementation.md +66 -0
- package/skills/supabase-reference-architecture/SKILL.md +249 -182
- package/skills/supabase-reference-architecture/references/errors.md +29 -0
- package/skills/supabase-reference-architecture/references/examples.md +116 -0
- package/skills/supabase-reference-architecture/references/key-components.md +244 -0
- package/skills/supabase-reference-architecture/references/project-structure.md +109 -0
- package/skills/supabase-reliability-patterns/SKILL.md +229 -234
- package/skills/supabase-reliability-patterns/references/circuit-breaker.md +36 -0
- package/skills/supabase-reliability-patterns/references/dead-letter-queue.md +48 -0
- package/skills/supabase-reliability-patterns/references/errors.md +11 -0
- package/skills/supabase-reliability-patterns/references/examples.md +11 -0
- package/skills/supabase-reliability-patterns/references/idempotency-keys.md +36 -0
- package/skills/supabase-reliability-patterns/references/offline-degradation-health-dualwrite.md +489 -0
- package/skills/supabase-schema-from-requirements/SKILL.md +373 -34
- package/skills/supabase-sdk-patterns/SKILL.md +388 -99
- package/skills/supabase-sdk-patterns/references/errors.md +11 -0
- package/skills/supabase-sdk-patterns/references/examples.md +45 -0
- package/skills/supabase-sdk-patterns/references/implementation.md +67 -0
- package/skills/supabase-security-basics/SKILL.md +282 -102
- package/skills/supabase-security-basics/references/errors.md +10 -0
- package/skills/supabase-security-basics/references/examples.md +70 -0
- package/skills/supabase-security-basics/references/implementation.md +39 -0
- package/skills/supabase-upgrade-migration/SKILL.md +248 -66
- package/skills/supabase-upgrade-migration/references/errors.md +10 -0
- package/skills/supabase-upgrade-migration/references/examples.md +51 -0
- package/skills/supabase-upgrade-migration/references/implementation.md +29 -0
- package/skills/supabase-webhooks-events/SKILL.md +412 -138
- package/skills/supabase-webhooks-events/references/errors.md +55 -0
- package/skills/supabase-webhooks-events/references/event-handler-pattern.md +106 -0
- package/skills/supabase-webhooks-events/references/examples.md +133 -0
- package/skills/supabase-webhooks-events/references/signature-verification.md +165 -0
|
@@ -1,119 +1,509 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: supabase-prod-checklist
|
|
3
|
-
description:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
description: 'Execute Supabase production deployment checklist covering RLS, key hygiene,
|
|
4
|
+
|
|
5
|
+
connection pooling, backups, monitoring, Edge Functions, and Storage policies.
|
|
6
|
+
|
|
7
|
+
Use when deploying to production, preparing for launch,
|
|
8
|
+
|
|
9
|
+
or auditing a live Supabase project for security and performance gaps.
|
|
10
|
+
|
|
11
|
+
Trigger with "supabase production", "supabase go-live",
|
|
12
|
+
|
|
13
|
+
"supabase launch checklist", "supabase prod ready", "deploy supabase",
|
|
14
|
+
|
|
15
|
+
"supabase production readiness".
|
|
16
|
+
|
|
17
|
+
'
|
|
18
|
+
allowed-tools: Read, Write, Edit, Bash(npx supabase:*), Bash(curl:*), Grep
|
|
10
19
|
version: 1.0.0
|
|
11
20
|
license: MIT
|
|
12
21
|
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
22
|
+
tags:
|
|
23
|
+
- saas
|
|
24
|
+
- supabase
|
|
25
|
+
- deployment
|
|
26
|
+
- production
|
|
27
|
+
- security
|
|
28
|
+
- rls
|
|
29
|
+
compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw
|
|
13
30
|
---
|
|
14
|
-
|
|
15
|
-
# Supabase Production Checklist
|
|
31
|
+
# Supabase Production Deployment Checklist
|
|
16
32
|
|
|
17
33
|
## Overview
|
|
18
|
-
|
|
34
|
+
|
|
35
|
+
Actionable 14-step checklist for taking a Supabase project to production. Covers RLS enforcement, key separation, connection pooling (Supavisor), backups/PITR, network restrictions, custom domains, auth emails, rate limits, monitoring, Edge Functions, Storage policies, indexes, and migrations. Based on Supabase's official [production guide](https://supabase.com/docs/guides/deployment/going-into-prod).
|
|
19
36
|
|
|
20
37
|
## Prerequisites
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
38
|
+
|
|
39
|
+
- Supabase project on Pro plan or higher (required for PITR, network restrictions)
|
|
40
|
+
- Separate production project (never share dev/prod)
|
|
41
|
+
- `@supabase/supabase-js` v2+ installed
|
|
42
|
+
- Supabase CLI installed (`npx supabase --version`)
|
|
43
|
+
- Domain and DNS configured for custom domain
|
|
44
|
+
- Deployment platform ready (Vercel, Netlify, Cloudflare, etc.)
|
|
25
45
|
|
|
26
46
|
## Instructions
|
|
27
47
|
|
|
28
|
-
### Step 1:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
### Step 1: Enforce Row Level Security on ALL Tables
|
|
49
|
+
|
|
50
|
+
RLS is the single most critical production requirement. Without it, any client with your anon key can read/write every row.
|
|
51
|
+
|
|
52
|
+
```sql
|
|
53
|
+
-- Audit: find tables WITHOUT RLS enabled
|
|
54
|
+
-- This query MUST return zero rows before going live
|
|
55
|
+
SELECT schemaname, tablename, rowsecurity
|
|
56
|
+
FROM pg_tables
|
|
57
|
+
WHERE schemaname = 'public' AND rowsecurity = false;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```sql
|
|
61
|
+
-- Enable RLS on a table
|
|
62
|
+
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
|
|
63
|
+
|
|
64
|
+
-- Create a basic read policy (authenticated users see own rows)
|
|
65
|
+
CREATE POLICY "Users can view own profile"
|
|
66
|
+
ON public.profiles
|
|
67
|
+
FOR SELECT
|
|
68
|
+
USING (auth.uid() = user_id);
|
|
69
|
+
|
|
70
|
+
-- Create an insert policy
|
|
71
|
+
CREATE POLICY "Users can insert own profile"
|
|
72
|
+
ON public.profiles
|
|
73
|
+
FOR INSERT
|
|
74
|
+
WITH CHECK (auth.uid() = user_id);
|
|
75
|
+
|
|
76
|
+
-- Create an update policy
|
|
77
|
+
CREATE POLICY "Users can update own profile"
|
|
78
|
+
ON public.profiles
|
|
79
|
+
FOR UPDATE
|
|
80
|
+
USING (auth.uid() = user_id)
|
|
81
|
+
WITH CHECK (auth.uid() = user_id);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- [ ] RLS enabled on every public table (zero rows from audit query above)
|
|
85
|
+
- [ ] SELECT, INSERT, UPDATE, DELETE policies defined for each table
|
|
86
|
+
- [ ] Policies tested with both authenticated and anonymous roles
|
|
87
|
+
- [ ] No tables use `USING (true)` without intent (public read tables only)
|
|
88
|
+
|
|
89
|
+
### Step 2: Enforce Key Separation — Anon vs Service Role
|
|
90
|
+
|
|
91
|
+
The `anon` key is safe for client-side code. The `service_role` key bypasses RLS entirely and must never leave server-side environments.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// Client-side — ONLY use anon key
|
|
95
|
+
import { createClient } from '@supabase/supabase-js';
|
|
96
|
+
|
|
97
|
+
const supabase = createClient(
|
|
98
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
99
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! // Safe for browsers
|
|
100
|
+
);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Server-side only — service_role key (API routes, webhooks, cron jobs)
|
|
105
|
+
import { createClient } from '@supabase/supabase-js';
|
|
106
|
+
|
|
107
|
+
const supabaseAdmin = createClient(
|
|
108
|
+
process.env.SUPABASE_URL!,
|
|
109
|
+
process.env.SUPABASE_SERVICE_ROLE_KEY!, // NEVER expose to client
|
|
110
|
+
{ auth: { autoRefreshToken: false, persistSession: false } }
|
|
111
|
+
);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
- [ ] Anon key used in all client-side code (`NEXT_PUBLIC_` prefix)
|
|
115
|
+
- [ ] Service role key used only in server-side code (API routes, Edge Functions)
|
|
116
|
+
- [ ] Service role key not in any client bundle (verify with `grep -r "service_role" dist/`)
|
|
117
|
+
- [ ] Database password changed from the auto-generated default
|
|
118
|
+
|
|
119
|
+
### Step 3: Configure Connection Pooling (Supavisor)
|
|
120
|
+
|
|
121
|
+
Supabase uses Supavisor for connection pooling. Serverless functions (Vercel, Netlify, Cloudflare Workers) MUST use the pooled connection string to avoid exhausting the database connection limit.
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
# Direct connection (migrations, admin tasks only)
|
|
125
|
+
postgresql://postgres:[PASSWORD]@db.[REF].supabase.co:5432/postgres
|
|
126
|
+
|
|
127
|
+
# Pooled connection via Supavisor (application code — USE THIS)
|
|
128
|
+
# Port 6543 = Supavisor pooler (vs 5432 direct)
|
|
129
|
+
postgresql://postgres.[REF]:[PASSWORD]@aws-0-us-east-1.pooler.supabase.com:6543/postgres
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// For serverless environments — use pooled connection
|
|
134
|
+
const supabase = createClient(
|
|
135
|
+
process.env.SUPABASE_URL!,
|
|
136
|
+
process.env.SUPABASE_ANON_KEY!,
|
|
137
|
+
{
|
|
138
|
+
db: { schema: 'public' },
|
|
139
|
+
// Supavisor handles pooling at port 6543
|
|
140
|
+
// No need to configure pgBouncer settings in the client
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- [ ] Application code uses pooled connection string (port 6543)
|
|
146
|
+
- [ ] Direct connection reserved for migrations and admin tasks only
|
|
147
|
+
- [ ] Connection string in deployment platform env vars (not hardcoded)
|
|
148
|
+
- [ ] Verified pool mode: `transaction` for serverless, `session` for long-lived connections
|
|
149
|
+
|
|
150
|
+
### Step 4: Enable Database Backups
|
|
151
|
+
|
|
152
|
+
Supabase provides automatic daily backups on Pro plan. Point-in-time recovery (PITR) enables granular restores.
|
|
153
|
+
|
|
154
|
+
- [ ] Automatic daily backups enabled (Pro plan — verify in Dashboard > Database > Backups)
|
|
155
|
+
- [ ] Point-in-time recovery configured (Dashboard > Database > Backups > PITR)
|
|
156
|
+
- [ ] Tested restore procedure on a staging project (do not skip this)
|
|
157
|
+
- [ ] Migration files committed to version control (`supabase/migrations/` directory)
|
|
158
|
+
- [ ] `npx supabase db push` tested against a fresh project to verify migrations replay cleanly
|
|
159
|
+
|
|
160
|
+
### Step 5: Configure Network Restrictions
|
|
161
|
+
|
|
162
|
+
Restrict database access to known IP addresses. This prevents unauthorized direct database connections even if credentials leak.
|
|
163
|
+
|
|
164
|
+
- [ ] IP allowlist configured (Dashboard > Database > Network Restrictions)
|
|
165
|
+
- [ ] Only deployment platform IPs and team office IPs are allowed
|
|
166
|
+
- [ ] Verified that application still connects after restrictions applied
|
|
167
|
+
- [ ] Documented which IPs are allowed and why
|
|
168
|
+
|
|
169
|
+
### Step 6: Configure Custom Domain
|
|
170
|
+
|
|
171
|
+
A custom domain replaces the default `*.supabase.co` URLs with your brand domain for API and auth endpoints.
|
|
172
|
+
|
|
173
|
+
- [ ] Custom domain configured (Dashboard > Settings > Custom Domains)
|
|
174
|
+
- [ ] DNS CNAME record added and verified
|
|
175
|
+
- [ ] SSL certificate provisioned and active
|
|
176
|
+
- [ ] Application code updated to use custom domain URL
|
|
177
|
+
- [ ] OAuth redirect URLs updated to use custom domain
|
|
178
|
+
|
|
179
|
+
### Step 7: Customize Auth Email Templates
|
|
180
|
+
|
|
181
|
+
Default Supabase auth emails show generic branding. Customize them so users see your domain and brand.
|
|
182
|
+
|
|
183
|
+
- [ ] Confirmation email template customized (Dashboard > Auth > Email Templates)
|
|
184
|
+
- [ ] Password reset email template customized
|
|
185
|
+
- [ ] Magic link email template customized
|
|
186
|
+
- [ ] Invite email template customized
|
|
187
|
+
- [ ] Custom SMTP configured (Dashboard > Auth > SMTP Settings) — avoids rate limits and improves deliverability
|
|
188
|
+
- [ ] Email confirmation enabled (Dashboard > Auth > Settings)
|
|
189
|
+
- [ ] OAuth redirect URLs restricted to production domains only
|
|
190
|
+
- [ ] Unused auth providers disabled
|
|
191
|
+
|
|
192
|
+
### Step 8: Understand Rate Limits Per Tier
|
|
193
|
+
|
|
194
|
+
Supabase enforces rate limits that vary by plan. Hitting these in production causes 429 errors.
|
|
195
|
+
|
|
196
|
+
| Resource | Free | Pro | Team |
|
|
197
|
+
|----------|------|-----|------|
|
|
198
|
+
| API requests | 500/min | 1,000/min | 5,000/min |
|
|
199
|
+
| Auth emails | 4/hour | 30/hour | 100/hour |
|
|
200
|
+
| Realtime connections | 200 concurrent | 500 concurrent | 2,000 concurrent |
|
|
201
|
+
| Edge Function invocations | 500K/month | 2M/month | 5M/month |
|
|
202
|
+
| Storage bandwidth | 2GB/month | 250GB/month | Custom |
|
|
203
|
+
| Database size | 500MB | 8GB | 50GB |
|
|
204
|
+
|
|
205
|
+
- [ ] Rate limits documented for your plan tier
|
|
206
|
+
- [ ] Client-side retry logic with exponential backoff for 429 responses
|
|
207
|
+
- [ ] Auth email rate limits understood (use custom SMTP to increase)
|
|
208
|
+
- [ ] Realtime connection limits planned for expected concurrent users
|
|
209
|
+
|
|
210
|
+
### Step 9: Review Monitoring Dashboards
|
|
211
|
+
|
|
212
|
+
Supabase provides built-in monitoring. Review these before launch to establish baselines.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// Health check endpoint — deploy this to your application
|
|
216
|
+
import { createClient } from '@supabase/supabase-js';
|
|
217
|
+
|
|
218
|
+
const supabase = createClient(
|
|
219
|
+
process.env.SUPABASE_URL!,
|
|
220
|
+
process.env.SUPABASE_ANON_KEY!
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
export async function GET() {
|
|
224
|
+
const start = Date.now();
|
|
225
|
+
const { data, error } = await supabase
|
|
226
|
+
.from('_health_check') // Create a small table for this
|
|
227
|
+
.select('id')
|
|
228
|
+
.limit(1);
|
|
229
|
+
|
|
230
|
+
const latency = Date.now() - start;
|
|
231
|
+
|
|
232
|
+
return Response.json({
|
|
233
|
+
status: error ? 'unhealthy' : 'healthy',
|
|
234
|
+
latency_ms: latency,
|
|
235
|
+
timestamp: new Date().toISOString(),
|
|
236
|
+
supabase_reachable: !error,
|
|
237
|
+
}, { status: error ? 503 : 200 });
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
- [ ] Dashboard > Reports reviewed (API requests, auth, storage, realtime)
|
|
242
|
+
- [ ] Dashboard > Logs > API checked for error patterns
|
|
243
|
+
- [ ] Dashboard > Database > Performance Advisor reviewed and recommendations applied
|
|
244
|
+
- [ ] Health check endpoint deployed and monitored (uptime service)
|
|
245
|
+
- [ ] Error tracking configured (Sentry, LogRocket, etc.)
|
|
246
|
+
- [ ] Alerts set for: error rate spikes, high latency, connection pool exhaustion
|
|
247
|
+
|
|
248
|
+
### Step 10: Deploy Edge Functions with Proper Env Vars
|
|
249
|
+
|
|
250
|
+
Edge Functions run on Deno Deploy. Environment variables must be set via the Supabase CLI or Dashboard, not hardcoded.
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# Set secrets for Edge Functions
|
|
254
|
+
npx supabase secrets set STRIPE_SECRET_KEY=sk_live_...
|
|
255
|
+
npx supabase secrets set RESEND_API_KEY=re_...
|
|
256
|
+
|
|
257
|
+
# List current secrets
|
|
258
|
+
npx supabase secrets list
|
|
259
|
+
|
|
260
|
+
# Deploy all Edge Functions
|
|
261
|
+
npx supabase functions deploy
|
|
262
|
+
|
|
263
|
+
# Deploy a specific function
|
|
264
|
+
npx supabase functions deploy process-webhook
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
// supabase/functions/process-webhook/index.ts
|
|
269
|
+
import { createClient } from '@supabase/supabase-js';
|
|
270
|
+
|
|
271
|
+
Deno.serve(async (req) => {
|
|
272
|
+
const supabase = createClient(
|
|
273
|
+
Deno.env.get('SUPABASE_URL')!,
|
|
274
|
+
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! // Available automatically
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
const body = await req.json();
|
|
278
|
+
// Process webhook payload...
|
|
279
|
+
|
|
280
|
+
return new Response(JSON.stringify({ received: true }), {
|
|
281
|
+
headers: { 'Content-Type': 'application/json' },
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
- [ ] All Edge Functions deployed to production (`npx supabase functions deploy`)
|
|
287
|
+
- [ ] Environment secrets set via `npx supabase secrets set` (not hardcoded)
|
|
288
|
+
- [ ] `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` available automatically (no need to set)
|
|
289
|
+
- [ ] Edge Functions tested with `npx supabase functions serve` locally before deploying
|
|
290
|
+
- [ ] CORS headers configured for Edge Functions that receive browser requests
|
|
291
|
+
|
|
292
|
+
### Step 11: Verify Storage Bucket Policies
|
|
293
|
+
|
|
294
|
+
Storage buckets need explicit policies, similar to RLS on tables. Without policies, buckets are inaccessible (default deny).
|
|
295
|
+
|
|
296
|
+
```sql
|
|
297
|
+
-- Check storage bucket configurations
|
|
298
|
+
SELECT id, name, public, file_size_limit, allowed_mime_types
|
|
299
|
+
FROM storage.buckets;
|
|
300
|
+
|
|
301
|
+
-- Check existing storage policies
|
|
302
|
+
SELECT policyname, tablename, cmd, qual
|
|
303
|
+
FROM pg_policies
|
|
304
|
+
WHERE schemaname = 'storage';
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
```sql
|
|
308
|
+
-- Example: Allow authenticated users to upload to their own folder
|
|
309
|
+
CREATE POLICY "Users can upload own files"
|
|
310
|
+
ON storage.objects
|
|
311
|
+
FOR INSERT
|
|
312
|
+
WITH CHECK (
|
|
313
|
+
bucket_id = 'avatars'
|
|
314
|
+
AND auth.uid()::text = (storage.foldername(name))[1]
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
-- Example: Allow public read access to a bucket
|
|
318
|
+
CREATE POLICY "Public read access"
|
|
319
|
+
ON storage.objects
|
|
320
|
+
FOR SELECT
|
|
321
|
+
USING (bucket_id = 'public-assets');
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
- [ ] Each bucket has explicit SELECT/INSERT/UPDATE/DELETE policies
|
|
325
|
+
- [ ] Public buckets are intentionally public (not accidentally open)
|
|
326
|
+
- [ ] File size limits set per bucket (`file_size_limit` in bucket config)
|
|
327
|
+
- [ ] Allowed MIME types restricted per bucket (`allowed_mime_types`)
|
|
328
|
+
- [ ] User upload paths scoped to `auth.uid()` to prevent overwrites
|
|
329
|
+
|
|
330
|
+
### Step 12: Add Database Indexes on Frequently Queried Columns
|
|
331
|
+
|
|
332
|
+
Missing indexes are the leading cause of slow queries after launch. Add indexes on foreign keys, filter columns, and sort columns.
|
|
333
|
+
|
|
334
|
+
```sql
|
|
335
|
+
-- Find missing indexes on foreign keys
|
|
336
|
+
SELECT
|
|
337
|
+
tc.table_name, kcu.column_name,
|
|
338
|
+
CASE WHEN i.indexname IS NULL THEN '** MISSING INDEX **' ELSE i.indexname END AS index_status
|
|
339
|
+
FROM information_schema.table_constraints tc
|
|
340
|
+
JOIN information_schema.key_column_usage kcu
|
|
341
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
342
|
+
LEFT JOIN pg_indexes i
|
|
343
|
+
ON i.tablename = tc.table_name
|
|
344
|
+
AND i.indexdef LIKE '%' || kcu.column_name || '%'
|
|
345
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
346
|
+
AND tc.table_schema = 'public';
|
|
347
|
+
|
|
348
|
+
-- Find slow queries (requires pg_stat_statements extension)
|
|
349
|
+
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
|
|
350
|
+
|
|
351
|
+
SELECT query, calls, mean_exec_time::numeric(10,2) AS avg_ms,
|
|
352
|
+
total_exec_time::numeric(10,2) AS total_ms
|
|
353
|
+
FROM pg_stat_statements
|
|
354
|
+
ORDER BY mean_exec_time DESC
|
|
355
|
+
LIMIT 10;
|
|
356
|
+
|
|
357
|
+
-- Check table bloat (dead tuples from updates/deletes)
|
|
358
|
+
SELECT relname, n_live_tup, n_dead_tup,
|
|
359
|
+
round(n_dead_tup::numeric / greatest(n_live_tup, 1) * 100, 1) AS dead_pct
|
|
360
|
+
FROM pg_stat_user_tables
|
|
361
|
+
WHERE n_dead_tup > 1000
|
|
362
|
+
ORDER BY n_dead_tup DESC;
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
```sql
|
|
366
|
+
-- Create indexes on commonly filtered columns
|
|
367
|
+
CREATE INDEX idx_profiles_user_id ON public.profiles(user_id);
|
|
368
|
+
CREATE INDEX idx_orders_created_at ON public.orders(created_at DESC);
|
|
369
|
+
CREATE INDEX idx_posts_status ON public.posts(status) WHERE status = 'published'; -- Partial index
|
|
370
|
+
|
|
371
|
+
-- Set query timeout for the authenticated role
|
|
372
|
+
ALTER ROLE authenticated SET statement_timeout = '10s';
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
- [ ] Indexes on all foreign key columns
|
|
376
|
+
- [ ] Indexes on columns used in WHERE, ORDER BY, and JOIN clauses
|
|
377
|
+
- [ ] `pg_stat_statements` enabled for ongoing query monitoring
|
|
378
|
+
- [ ] Performance Advisor reviewed (Dashboard > Database > Performance)
|
|
379
|
+
- [ ] `statement_timeout` set for authenticated role to prevent runaway queries
|
|
380
|
+
- [ ] Table bloat checked — VACUUM if dead tuple percentage > 10%
|
|
381
|
+
|
|
382
|
+
### Step 13: Apply Migrations with `npx supabase db push`
|
|
383
|
+
|
|
384
|
+
All schema changes must go through migration files, never manual Dashboard edits in production.
|
|
385
|
+
|
|
55
386
|
```bash
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
387
|
+
# Generate a migration from local changes
|
|
388
|
+
npx supabase db diff --use-migra -f add_indexes
|
|
389
|
+
|
|
390
|
+
# Apply migrations to production (linked project)
|
|
391
|
+
npx supabase db push
|
|
392
|
+
|
|
393
|
+
# Verify migration history
|
|
394
|
+
npx supabase migration list
|
|
395
|
+
|
|
396
|
+
# If a migration fails, create a rollback
|
|
397
|
+
npx supabase migration new rollback_bad_change
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
- [ ] All schema changes in `supabase/migrations/` directory (version controlled)
|
|
401
|
+
- [ ] `npx supabase db push` tested against a fresh project
|
|
402
|
+
- [ ] Migration history matches between local and remote (`npx supabase migration list`)
|
|
403
|
+
- [ ] Rollback migration prepared for risky schema changes
|
|
404
|
+
- [ ] No manual schema edits in production Dashboard
|
|
59
405
|
|
|
60
|
-
|
|
61
|
-
kubectl apply -f k8s/production.yaml
|
|
62
|
-
kubectl set image deployment/supabase-integration app=image:new --record
|
|
63
|
-
kubectl rollout pause deployment/supabase-integration
|
|
406
|
+
### Step 14: Pre-Launch Final Verification
|
|
64
407
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
408
|
+
```bash
|
|
409
|
+
# Verify RLS status one final time
|
|
410
|
+
npx supabase inspect db table-sizes --linked
|
|
68
411
|
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
kubectl rollout pause deployment/supabase-integration
|
|
72
|
-
sleep 300
|
|
412
|
+
# Check that the project is linked to production
|
|
413
|
+
npx supabase status
|
|
73
414
|
|
|
74
|
-
#
|
|
75
|
-
|
|
76
|
-
kubectl rollout status deployment/supabase-integration
|
|
415
|
+
# Verify connection string works
|
|
416
|
+
npx supabase db ping --linked
|
|
77
417
|
```
|
|
78
418
|
|
|
419
|
+
- [ ] CORS settings match production domain (Dashboard > API > CORS)
|
|
420
|
+
- [ ] Environment variables set correctly in deployment platform
|
|
421
|
+
- [ ] Realtime enabled only on tables that need it (reduces connection usage)
|
|
422
|
+
- [ ] Webhook endpoints registered and tested
|
|
423
|
+
- [ ] Load test completed on staging (see `supabase-load-scale`)
|
|
424
|
+
- [ ] SSL enforcement enabled (Dashboard > Database > Settings > SSL)
|
|
425
|
+
- [ ] DNS and custom domain verified end-to-end
|
|
426
|
+
|
|
79
427
|
## Output
|
|
80
|
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
428
|
+
|
|
429
|
+
- All 14 checklist sections verified with zero unchecked items
|
|
430
|
+
- RLS enforced on every public table with tested policies
|
|
431
|
+
- Key separation verified (anon client-side, service_role server-side only)
|
|
432
|
+
- Connection pooling via Supavisor (port 6543) for all application code
|
|
433
|
+
- Backups, PITR, monitoring, Edge Functions, Storage policies, indexes all verified
|
|
434
|
+
- Migrations applied cleanly via `npx supabase db push`
|
|
84
435
|
|
|
85
436
|
## Error Handling
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
437
|
+
|
|
438
|
+
| Issue | Cause | Solution |
|
|
439
|
+
|-------|-------|----------|
|
|
440
|
+
| `403 Forbidden` on all API calls | RLS enabled but no policies created | Add SELECT/INSERT/UPDATE/DELETE policies for each role |
|
|
441
|
+
| `429 Too Many Requests` | Plan rate limit exceeded | Upgrade plan or implement client-side backoff with retry |
|
|
442
|
+
| Connection timeout under load | Using direct connection in serverless | Switch to pooled connection string (port 6543) |
|
|
443
|
+
| Auth emails not delivered | Default SMTP rate-limited | Configure custom SMTP provider (SendGrid, Resend, Postmark) |
|
|
444
|
+
| `PGRST301` permission denied | Service role key used where anon expected | Check client initialization — use anon key for client-side |
|
|
445
|
+
| Edge Function cold starts | First invocation after idle period | Pre-warm with scheduled pings or accept ~200ms cold start |
|
|
446
|
+
| Storage upload fails | Missing bucket policy or size limit exceeded | Add INSERT policy and check `file_size_limit` on bucket |
|
|
447
|
+
| Slow queries after launch | Missing indexes on filter/join columns | Run Performance Advisor and add indexes per Step 12 |
|
|
448
|
+
| Migration conflicts | Manual Dashboard edits diverged from migration files | Run `npx supabase db diff` to capture drift, then commit |
|
|
92
449
|
|
|
93
450
|
## Examples
|
|
94
451
|
|
|
95
|
-
###
|
|
452
|
+
### Client Setup (Next.js)
|
|
453
|
+
|
|
96
454
|
```typescript
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
455
|
+
// lib/supabase/client.ts — browser (anon key)
|
|
456
|
+
import { createClient } from '@supabase/supabase-js';
|
|
457
|
+
export const supabase = createClient<Database>(
|
|
458
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
459
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
// lib/supabase/server.ts — server only (service role)
|
|
463
|
+
export const supabaseAdmin = createClient<Database>(
|
|
464
|
+
process.env.SUPABASE_URL!,
|
|
465
|
+
process.env.SUPABASE_SERVICE_ROLE_KEY!,
|
|
466
|
+
{ auth: { autoRefreshToken: false, persistSession: false } }
|
|
467
|
+
);
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### RLS Policy Pattern
|
|
471
|
+
|
|
472
|
+
```sql
|
|
473
|
+
ALTER TABLE public.posts ENABLE ROW LEVEL SECURITY;
|
|
474
|
+
CREATE POLICY "Public read published" ON public.posts
|
|
475
|
+
FOR SELECT USING (status = 'published');
|
|
476
|
+
CREATE POLICY "Authors manage own" ON public.posts
|
|
477
|
+
FOR ALL USING (auth.uid() = author_id)
|
|
478
|
+
WITH CHECK (auth.uid() = author_id);
|
|
106
479
|
```
|
|
107
480
|
|
|
108
|
-
###
|
|
481
|
+
### Rollback
|
|
482
|
+
|
|
109
483
|
```bash
|
|
110
|
-
|
|
111
|
-
|
|
484
|
+
npx supabase migration new rollback_bad_change # Create reversal SQL
|
|
485
|
+
npx supabase db push # Apply rollback
|
|
486
|
+
# For data: Dashboard > Database > Backups > PITR
|
|
487
|
+
# For app: vercel rollback / netlify deploy --prod
|
|
112
488
|
```
|
|
113
489
|
|
|
490
|
+
For complete examples including health checks, storage policies, and Edge Functions, see [examples.md](references/examples.md).
|
|
491
|
+
|
|
114
492
|
## Resources
|
|
115
|
-
|
|
116
|
-
- [
|
|
493
|
+
|
|
494
|
+
- [Going to Production](https://supabase.com/docs/guides/deployment/going-into-prod) — Official production checklist
|
|
495
|
+
- [Maturity Model](https://supabase.com/docs/guides/deployment/maturity-model) — Project lifecycle stages
|
|
496
|
+
- [Shared Responsibility Model](https://supabase.com/docs/guides/deployment/shared-responsibility-model) — What Supabase manages vs. what you manage
|
|
497
|
+
- [Performance Advisor](https://supabase.com/docs/guides/database/inspect) — Built-in query analysis
|
|
498
|
+
- [Connection Pooling](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler) — Supavisor configuration
|
|
499
|
+
- [RLS Guide](https://supabase.com/docs/guides/database/postgres/row-level-security) — Policy patterns and examples
|
|
500
|
+
- [Edge Functions](https://supabase.com/docs/guides/functions) — Serverless Deno functions
|
|
501
|
+
- [Storage](https://supabase.com/docs/guides/storage) — File storage with policies
|
|
502
|
+
- [`@supabase/supabase-js` Reference](https://supabase.com/docs/reference/javascript/introduction) — Client SDK docs
|
|
117
503
|
|
|
118
504
|
## Next Steps
|
|
119
|
-
|
|
505
|
+
|
|
506
|
+
- For SDK version upgrades, see `supabase-upgrade-migration`
|
|
507
|
+
- For load testing before launch, see `supabase-load-scale`
|
|
508
|
+
- For monitoring in production, see `supabase-monitoring`
|
|
509
|
+
- For Edge Function patterns, see `supabase-edge-functions`
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Error Handling Reference
|
|
2
|
+
|
|
3
|
+
## Production Error Catalog
|
|
4
|
+
|
|
5
|
+
| Error | HTTP Status | Cause | Solution |
|
|
6
|
+
|-------|------------|-------|----------|
|
|
7
|
+
| `403 Forbidden` | 403 | RLS enabled but no policies created | Add SELECT/INSERT/UPDATE/DELETE policies for each role |
|
|
8
|
+
| `429 Too Many Requests` | 429 | Plan rate limit exceeded | Upgrade plan or implement client-side exponential backoff |
|
|
9
|
+
| Connection timeout | — | Direct connection in serverless | Switch to pooled connection string (Supavisor port 6543) |
|
|
10
|
+
| `PGRST301` permission denied | 401 | Service role key used where anon expected | Verify client uses anon key for client-side, service_role for server only |
|
|
11
|
+
| Auth emails not delivered | — | Default SMTP rate-limited (4/hour free) | Configure custom SMTP: SendGrid, Resend, or Postmark |
|
|
12
|
+
| Edge Function cold start | — | First invocation after idle period | Pre-warm with scheduled pings or accept ~200ms delay |
|
|
13
|
+
| Storage upload fails | 400/403 | Missing bucket policy or size limit exceeded | Add INSERT policy on `storage.objects` and check `file_size_limit` |
|
|
14
|
+
| Slow queries | — | Missing indexes on filter/join columns | Run Performance Advisor, add indexes per foreign keys and WHERE clauses |
|
|
15
|
+
| Migration conflicts | — | Manual Dashboard edits diverged from files | Run `npx supabase db diff` to capture drift, commit as migration |
|
|
16
|
+
| Realtime disconnects | — | Concurrent connection limit exceeded per tier | Reduce subscriptions or upgrade plan tier |
|
|
17
|
+
|
|
18
|
+
## Alert Thresholds
|
|
19
|
+
|
|
20
|
+
| Alert | Condition | Severity | Action |
|
|
21
|
+
|-------|-----------|----------|--------|
|
|
22
|
+
| API Down | 5xx errors > 10/min | P1 — Critical | Check Supabase status page, verify Edge Functions, check DB connections |
|
|
23
|
+
| High Latency | p99 > 5000ms | P2 — Warning | Run Performance Advisor, check missing indexes, verify connection pooling |
|
|
24
|
+
| Rate Limited | 429 errors > 5/min | P2 — Warning | Implement backoff, consider plan upgrade, audit request patterns |
|
|
25
|
+
| Auth Failures | 401/403 errors spike | P1 — Critical | Check RLS policies, verify JWT expiry, audit auth provider settings |
|
|
26
|
+
| Pool Exhaustion | Active connections > 80% pool | P1 — Critical | Switch to Supavisor pooled connection, reduce direct connections |
|
|
27
|
+
| Storage Quota | Usage > 80% of plan limit | P3 — Info | Clean unused files, upgrade plan, implement lifecycle policies |
|
|
28
|
+
|
|
29
|
+
## Supabase Error Code Reference
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// Common Supabase error shapes
|
|
33
|
+
interface SupabaseError {
|
|
34
|
+
message: string;
|
|
35
|
+
details: string | null;
|
|
36
|
+
hint: string | null;
|
|
37
|
+
code: string; // PostgreSQL error code
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Handle errors in application code
|
|
41
|
+
const { data, error } = await supabase.from('posts').select('*');
|
|
42
|
+
if (error) {
|
|
43
|
+
switch (error.code) {
|
|
44
|
+
case '42501': // insufficient_privilege — RLS blocking
|
|
45
|
+
console.error('RLS policy missing or too restrictive');
|
|
46
|
+
break;
|
|
47
|
+
case '23505': // unique_violation
|
|
48
|
+
console.error('Duplicate record:', error.details);
|
|
49
|
+
break;
|
|
50
|
+
case '23503': // foreign_key_violation
|
|
51
|
+
console.error('Referenced record does not exist');
|
|
52
|
+
break;
|
|
53
|
+
case '57014': // statement_timeout
|
|
54
|
+
console.error('Query timed out — add index or optimize');
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
console.error('Supabase error:', error.message);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
*[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
|