@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
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Operational Patterns — Edge Functions, Caching, Queues, Audit
|
|
2
|
+
|
|
3
|
+
## Edge Functions for Business Logic
|
|
4
|
+
|
|
5
|
+
Move business logic close to the database. Edge Functions run on Deno in the same region as your Supabase project.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// supabase/functions/process-order/index.ts
|
|
9
|
+
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'
|
|
10
|
+
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
|
|
11
|
+
|
|
12
|
+
serve(async (req) => {
|
|
13
|
+
const supabase = createClient(
|
|
14
|
+
Deno.env.get('SUPABASE_URL')!,
|
|
15
|
+
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const { order_id } = await req.json()
|
|
19
|
+
|
|
20
|
+
// All business logic in one function, close to the database
|
|
21
|
+
const { data: order, error } = await supabase
|
|
22
|
+
.from('orders')
|
|
23
|
+
.select('*, items:order_items(*)')
|
|
24
|
+
.eq('id', order_id)
|
|
25
|
+
.single()
|
|
26
|
+
|
|
27
|
+
if (error) return new Response(JSON.stringify({ error: error.message }), { status: 400 })
|
|
28
|
+
|
|
29
|
+
// Calculate totals
|
|
30
|
+
const total = order.items.reduce(
|
|
31
|
+
(sum: number, item: { quantity: number; unit_price: number }) =>
|
|
32
|
+
sum + item.quantity * item.unit_price,
|
|
33
|
+
0
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
// Update order with calculated total and mark as processed
|
|
37
|
+
const { error: updateError } = await supabase
|
|
38
|
+
.from('orders')
|
|
39
|
+
.update({ total, status: 'processed', processed_at: new Date().toISOString() })
|
|
40
|
+
.eq('id', order_id)
|
|
41
|
+
|
|
42
|
+
if (updateError) return new Response(JSON.stringify({ error: updateError.message }), { status: 500 })
|
|
43
|
+
|
|
44
|
+
return new Response(JSON.stringify({ order_id, total, status: 'processed' }), {
|
|
45
|
+
headers: { 'Content-Type': 'application/json' }
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Deploy with: `supabase functions deploy process-order`
|
|
51
|
+
|
|
52
|
+
## Server-Side Caching for Frequent Reads
|
|
53
|
+
|
|
54
|
+
Reduce database load for frequently-read, rarely-changed data.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// lib/cached-queries.ts
|
|
58
|
+
import { getSupabaseAdmin } from './admin'
|
|
59
|
+
|
|
60
|
+
interface CacheEntry<T> {
|
|
61
|
+
data: T
|
|
62
|
+
expiresAt: number
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const cache = new Map<string, CacheEntry<unknown>>()
|
|
66
|
+
|
|
67
|
+
export async function cachedQuery<T>(
|
|
68
|
+
key: string,
|
|
69
|
+
queryFn: () => Promise<T>,
|
|
70
|
+
ttlMs: number = 60_000 // Default 60s TTL
|
|
71
|
+
): Promise<T> {
|
|
72
|
+
const cached = cache.get(key) as CacheEntry<T> | undefined
|
|
73
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
74
|
+
return cached.data
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const data = await queryFn()
|
|
78
|
+
cache.set(key, { data, expiresAt: Date.now() + ttlMs })
|
|
79
|
+
return data
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Usage: cache tenant config for 5 minutes
|
|
83
|
+
export async function getTenantConfig(tenantId: string) {
|
|
84
|
+
return cachedQuery(
|
|
85
|
+
`tenant-config:${tenantId}`,
|
|
86
|
+
async () => {
|
|
87
|
+
const { data, error } = await getSupabaseAdmin()
|
|
88
|
+
.from('tenants')
|
|
89
|
+
.select('id, name, plan, feature_flags')
|
|
90
|
+
.eq('id', tenantId)
|
|
91
|
+
.single()
|
|
92
|
+
if (error) throw error
|
|
93
|
+
return data
|
|
94
|
+
},
|
|
95
|
+
5 * 60_000 // 5 minute TTL
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Queue Pattern with `pg_cron` and Custom Tables
|
|
101
|
+
|
|
102
|
+
Supabase does not have a built-in queue, but you can build a reliable one with a jobs table and `pg_cron`.
|
|
103
|
+
|
|
104
|
+
```sql
|
|
105
|
+
-- Migration: 20260104000000_create_job_queue.sql
|
|
106
|
+
|
|
107
|
+
create table public.job_queue (
|
|
108
|
+
id bigint generated always as identity primary key,
|
|
109
|
+
job_type text not null,
|
|
110
|
+
payload jsonb not null default '{}',
|
|
111
|
+
status text not null default 'pending'
|
|
112
|
+
check (status in ('pending', 'processing', 'completed', 'failed', 'dead')),
|
|
113
|
+
attempts int default 0,
|
|
114
|
+
max_attempts int default 3,
|
|
115
|
+
scheduled_at timestamptz default now(),
|
|
116
|
+
started_at timestamptz,
|
|
117
|
+
completed_at timestamptz,
|
|
118
|
+
error_message text,
|
|
119
|
+
created_at timestamptz default now()
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
create index idx_job_queue_pending
|
|
123
|
+
on public.job_queue (scheduled_at)
|
|
124
|
+
where status = 'pending';
|
|
125
|
+
|
|
126
|
+
-- Claim the next job atomically (prevents double-processing)
|
|
127
|
+
create or replace function public.claim_next_job(p_job_type text)
|
|
128
|
+
returns public.job_queue as $$
|
|
129
|
+
update public.job_queue
|
|
130
|
+
set status = 'processing',
|
|
131
|
+
started_at = now(),
|
|
132
|
+
attempts = attempts + 1
|
|
133
|
+
where id = (
|
|
134
|
+
select id from public.job_queue
|
|
135
|
+
where status = 'pending'
|
|
136
|
+
and job_type = p_job_type
|
|
137
|
+
and scheduled_at <= now()
|
|
138
|
+
and attempts < max_attempts
|
|
139
|
+
order by scheduled_at
|
|
140
|
+
for update skip locked
|
|
141
|
+
limit 1
|
|
142
|
+
)
|
|
143
|
+
returning *;
|
|
144
|
+
$$ language sql;
|
|
145
|
+
|
|
146
|
+
-- pg_cron: retry failed jobs every 5 minutes
|
|
147
|
+
select cron.schedule(
|
|
148
|
+
'retry-failed-jobs',
|
|
149
|
+
'*/5 * * * *',
|
|
150
|
+
$$
|
|
151
|
+
update public.job_queue
|
|
152
|
+
set status = 'pending'
|
|
153
|
+
where status = 'failed'
|
|
154
|
+
and attempts < max_attempts;
|
|
155
|
+
|
|
156
|
+
update public.job_queue
|
|
157
|
+
set status = 'dead'
|
|
158
|
+
where status = 'failed'
|
|
159
|
+
and attempts >= max_attempts;
|
|
160
|
+
$$
|
|
161
|
+
);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Audit Trail with Trigger-Based Logging
|
|
165
|
+
|
|
166
|
+
Automatically log every data change to an append-only audit table.
|
|
167
|
+
|
|
168
|
+
```sql
|
|
169
|
+
-- Migration: 20260105000000_create_audit_log.sql
|
|
170
|
+
|
|
171
|
+
create table public.audit_log (
|
|
172
|
+
id bigint generated always as identity primary key,
|
|
173
|
+
table_name text not null,
|
|
174
|
+
record_id text not null,
|
|
175
|
+
action text not null check (action in ('INSERT', 'UPDATE', 'DELETE')),
|
|
176
|
+
old_data jsonb,
|
|
177
|
+
new_data jsonb,
|
|
178
|
+
changed_fields text[],
|
|
179
|
+
user_id uuid references auth.users(id),
|
|
180
|
+
org_id uuid,
|
|
181
|
+
ip_address inet,
|
|
182
|
+
created_at timestamptz default now()
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
create index idx_audit_log_table_record on public.audit_log (table_name, record_id);
|
|
186
|
+
create index idx_audit_log_created on public.audit_log (created_at);
|
|
187
|
+
create index idx_audit_log_user on public.audit_log (user_id);
|
|
188
|
+
|
|
189
|
+
-- Generic audit trigger function
|
|
190
|
+
create or replace function public.audit_trigger_fn()
|
|
191
|
+
returns trigger as $$
|
|
192
|
+
declare
|
|
193
|
+
changed text[];
|
|
194
|
+
col text;
|
|
195
|
+
begin
|
|
196
|
+
if TG_OP = 'UPDATE' then
|
|
197
|
+
for col in select column_name from information_schema.columns
|
|
198
|
+
where table_schema = TG_TABLE_SCHEMA and table_name = TG_TABLE_NAME
|
|
199
|
+
loop
|
|
200
|
+
if to_jsonb(OLD) ->> col is distinct from to_jsonb(NEW) ->> col then
|
|
201
|
+
changed := array_append(changed, col);
|
|
202
|
+
end if;
|
|
203
|
+
end loop;
|
|
204
|
+
end if;
|
|
205
|
+
|
|
206
|
+
insert into public.audit_log (
|
|
207
|
+
table_name, record_id, action,
|
|
208
|
+
old_data, new_data, changed_fields,
|
|
209
|
+
user_id, org_id
|
|
210
|
+
) values (
|
|
211
|
+
TG_TABLE_NAME,
|
|
212
|
+
coalesce(to_jsonb(NEW) ->> 'id', to_jsonb(OLD) ->> 'id'),
|
|
213
|
+
TG_OP,
|
|
214
|
+
case when TG_OP in ('UPDATE', 'DELETE') then to_jsonb(OLD) end,
|
|
215
|
+
case when TG_OP in ('INSERT', 'UPDATE') then to_jsonb(NEW) end,
|
|
216
|
+
changed,
|
|
217
|
+
auth.uid(),
|
|
218
|
+
coalesce(
|
|
219
|
+
to_jsonb(NEW) ->> 'org_id',
|
|
220
|
+
to_jsonb(OLD) ->> 'org_id'
|
|
221
|
+
)::uuid
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
return coalesce(NEW, OLD);
|
|
225
|
+
end;
|
|
226
|
+
$$ language plpgsql security definer;
|
|
227
|
+
|
|
228
|
+
-- Attach to any table
|
|
229
|
+
create trigger audit_projects
|
|
230
|
+
after insert or update or delete on public.projects
|
|
231
|
+
for each row execute function public.audit_trigger_fn();
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Query the audit trail:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const { data: history } = await supabase
|
|
238
|
+
.from('audit_log')
|
|
239
|
+
.select('action, changed_fields, old_data, new_data, created_at')
|
|
240
|
+
.eq('table_name', 'projects')
|
|
241
|
+
.eq('record_id', projectId)
|
|
242
|
+
.order('created_at', { ascending: false })
|
|
243
|
+
.limit(50)
|
|
244
|
+
```
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Project Structure — Monorepo Layout
|
|
2
|
+
|
|
3
|
+
## Recommended Directory Layout
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
my-platform/
|
|
7
|
+
├── packages/
|
|
8
|
+
│ └── supabase/ # Shared Supabase package
|
|
9
|
+
│ ├── supabase/
|
|
10
|
+
│ │ ├── config.toml # Supabase CLI config
|
|
11
|
+
│ │ ├── migrations/ # Version-controlled SQL migrations
|
|
12
|
+
│ │ │ ├── 20260101000000_create_tenants.sql
|
|
13
|
+
│ │ │ ├── 20260102000000_create_profiles.sql
|
|
14
|
+
│ │ │ └── 20260103000000_create_audit_log.sql
|
|
15
|
+
│ │ ├── seed.sql # Development seed data
|
|
16
|
+
│ │ └── functions/ # Edge Functions (Deno runtime)
|
|
17
|
+
│ │ ├── process-order/index.ts
|
|
18
|
+
│ │ └── send-notification/index.ts
|
|
19
|
+
│ ├── src/
|
|
20
|
+
│ │ ├── client.ts # Client singleton (anon key)
|
|
21
|
+
│ │ ├── admin.ts # Admin client (service_role, server only)
|
|
22
|
+
│ │ ├── database.types.ts # Auto-generated from `supabase gen types`
|
|
23
|
+
│ │ └── errors.ts # Custom error classes
|
|
24
|
+
│ ├── package.json
|
|
25
|
+
│ └── tsconfig.json
|
|
26
|
+
├── apps/
|
|
27
|
+
│ ├── web/ # Next.js / SvelteKit frontend
|
|
28
|
+
│ ├── admin/ # Internal admin dashboard
|
|
29
|
+
│ └── worker/ # Background job runner
|
|
30
|
+
├── pnpm-workspace.yaml
|
|
31
|
+
└── turbo.json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Migration Management
|
|
35
|
+
|
|
36
|
+
Keep migrations in version control. Every schema change is a new migration file.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Generate a new migration after making changes in the dashboard
|
|
40
|
+
supabase db diff --use-migra -f add_orders_table
|
|
41
|
+
|
|
42
|
+
# Apply migrations locally
|
|
43
|
+
supabase db reset
|
|
44
|
+
|
|
45
|
+
# Push migrations to production
|
|
46
|
+
supabase db push
|
|
47
|
+
|
|
48
|
+
# Regenerate TypeScript types after any schema change
|
|
49
|
+
supabase gen types typescript --local > packages/supabase/src/database.types.ts
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Add a `turbo.json` pipeline so type generation runs after migrations:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"pipeline": {
|
|
57
|
+
"@my-platform/supabase#gen-types": {
|
|
58
|
+
"outputs": ["src/database.types.ts"],
|
|
59
|
+
"cache": false
|
|
60
|
+
},
|
|
61
|
+
"build": {
|
|
62
|
+
"dependsOn": ["@my-platform/supabase#gen-types"]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Microservices with Separate Supabase Projects
|
|
69
|
+
|
|
70
|
+
For large organizations, each service owns its own Supabase project. Cross-service reads use the `service_role` key (bypasses RLS).
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// services/billing/src/supabase.ts
|
|
74
|
+
import { createClient } from '@supabase/supabase-js'
|
|
75
|
+
import type { BillingDB } from './database.types'
|
|
76
|
+
|
|
77
|
+
export const billingDb = createClient<BillingDB>(
|
|
78
|
+
process.env.BILLING_SUPABASE_URL!,
|
|
79
|
+
process.env.BILLING_SUPABASE_SERVICE_ROLE_KEY!,
|
|
80
|
+
{ auth: { autoRefreshToken: false, persistSession: false } }
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
// Cross-project access: read user profiles from the main project
|
|
84
|
+
import type { MainDB } from '@my-platform/supabase'
|
|
85
|
+
|
|
86
|
+
const mainDb = createClient<MainDB>(
|
|
87
|
+
process.env.MAIN_SUPABASE_URL!,
|
|
88
|
+
process.env.MAIN_SUPABASE_SERVICE_ROLE_KEY!,
|
|
89
|
+
{ auth: { autoRefreshToken: false, persistSession: false } }
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
export async function getUserProfile(userId: string) {
|
|
93
|
+
const { data, error } = await mainDb
|
|
94
|
+
.from('profiles')
|
|
95
|
+
.select('id, email, display_name, avatar_url')
|
|
96
|
+
.eq('id', userId)
|
|
97
|
+
.single()
|
|
98
|
+
|
|
99
|
+
if (error) throw new Error(`Cross-project profile lookup failed: ${error.message}`)
|
|
100
|
+
return data
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Cross-project access rules:
|
|
105
|
+
|
|
106
|
+
- Always use `service_role` key (it bypasses RLS)
|
|
107
|
+
- Never expose `service_role` keys to client-side code
|
|
108
|
+
- Limit cross-project calls to read-only where possible
|
|
109
|
+
- Consider caching cross-project lookups to reduce latency
|