@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.
Files changed (133) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +73 -47
  3. package/package.json +4 -4
  4. package/skills/supabase-advanced-troubleshooting/SKILL.md +404 -200
  5. package/skills/supabase-advanced-troubleshooting/references/errors.md +11 -0
  6. package/skills/supabase-advanced-troubleshooting/references/evidence-collection-framework.md +34 -0
  7. package/skills/supabase-advanced-troubleshooting/references/examples.md +11 -0
  8. package/skills/supabase-advanced-troubleshooting/references/rls-edge-functions-realtime.md +363 -0
  9. package/skills/supabase-advanced-troubleshooting/references/systematic-isolation.md +56 -0
  10. package/skills/supabase-advanced-troubleshooting/references/timing-analysis.md +35 -0
  11. package/skills/supabase-architecture-variants/SKILL.md +395 -216
  12. package/skills/supabase-architecture-variants/references/errors.md +11 -0
  13. package/skills/supabase-architecture-variants/references/examples.md +12 -0
  14. package/skills/supabase-architecture-variants/references/serverless-and-multi-tenant.md +251 -0
  15. package/skills/supabase-architecture-variants/references/variant-a-monolith-(simple).md +44 -0
  16. package/skills/supabase-architecture-variants/references/variant-b-service-layer-(moderate).md +72 -0
  17. package/skills/supabase-architecture-variants/references/variant-c-microservice-(complex).md +81 -0
  18. package/skills/supabase-auth-storage-realtime-core/SKILL.md +471 -37
  19. package/skills/supabase-ci-integration/SKILL.md +315 -67
  20. package/skills/supabase-ci-integration/references/errors.md +10 -0
  21. package/skills/supabase-ci-integration/references/examples.md +36 -0
  22. package/skills/supabase-ci-integration/references/implementation.md +54 -0
  23. package/skills/supabase-common-errors/SKILL.md +320 -62
  24. package/skills/supabase-common-errors/references/errors.md +53 -0
  25. package/skills/supabase-common-errors/references/examples.md +23 -0
  26. package/skills/supabase-cost-tuning/SKILL.md +365 -131
  27. package/skills/supabase-cost-tuning/references/cost-estimation.md +34 -0
  28. package/skills/supabase-cost-tuning/references/cost-reduction-strategies.md +40 -0
  29. package/skills/supabase-cost-tuning/references/errors.md +11 -0
  30. package/skills/supabase-cost-tuning/references/examples.md +15 -0
  31. package/skills/supabase-data-handling/SKILL.md +378 -145
  32. package/skills/supabase-data-handling/references/errors.md +11 -0
  33. package/skills/supabase-data-handling/references/examples.md +27 -0
  34. package/skills/supabase-data-handling/references/implementation.md +223 -0
  35. package/skills/supabase-data-handling/references/retention-and-backup.md +221 -0
  36. package/skills/supabase-debug-bundle/SKILL.md +267 -73
  37. package/skills/supabase-debug-bundle/references/errors.md +12 -0
  38. package/skills/supabase-debug-bundle/references/examples.md +24 -0
  39. package/skills/supabase-debug-bundle/references/implementation.md +54 -0
  40. package/skills/supabase-deploy-integration/SKILL.md +258 -147
  41. package/skills/supabase-deploy-integration/references/errors.md +11 -0
  42. package/skills/supabase-deploy-integration/references/examples.md +21 -0
  43. package/skills/supabase-deploy-integration/references/google-cloud-run.md +36 -0
  44. package/skills/supabase-deploy-integration/references/vercel-deployment.md +35 -0
  45. package/skills/supabase-enterprise-rbac/SKILL.md +327 -160
  46. package/skills/supabase-enterprise-rbac/references/api-scoping-and-enforcement.md +255 -0
  47. package/skills/supabase-enterprise-rbac/references/errors.md +11 -0
  48. package/skills/supabase-enterprise-rbac/references/examples.md +12 -0
  49. package/skills/supabase-enterprise-rbac/references/role-implementation.md +33 -0
  50. package/skills/supabase-enterprise-rbac/references/sso-integration.md +35 -0
  51. package/skills/supabase-hello-world/SKILL.md +160 -54
  52. package/skills/supabase-incident-runbook/SKILL.md +453 -131
  53. package/skills/supabase-incident-runbook/references/errors.md +11 -0
  54. package/skills/supabase-incident-runbook/references/examples.md +10 -0
  55. package/skills/supabase-incident-runbook/references/immediate-actions-by-error-type.md +41 -0
  56. package/skills/supabase-install-auth/SKILL.md +186 -50
  57. package/skills/supabase-install-auth/references/examples.md +102 -0
  58. package/skills/supabase-known-pitfalls/SKILL.md +411 -241
  59. package/skills/supabase-known-pitfalls/references/errors.md +11 -0
  60. package/skills/supabase-known-pitfalls/references/examples.md +12 -0
  61. package/skills/supabase-load-scale/SKILL.md +346 -217
  62. package/skills/supabase-load-scale/references/capacity-planning.md +47 -0
  63. package/skills/supabase-load-scale/references/errors.md +11 -0
  64. package/skills/supabase-load-scale/references/examples.md +26 -0
  65. package/skills/supabase-load-scale/references/load-testing-with-k6.md +59 -0
  66. package/skills/supabase-load-scale/references/scaling-patterns.md +65 -0
  67. package/skills/supabase-load-scale/references/table-partitioning.md +263 -0
  68. package/skills/supabase-local-dev-loop/SKILL.md +272 -73
  69. package/skills/supabase-local-dev-loop/references/errors.md +11 -0
  70. package/skills/supabase-local-dev-loop/references/examples.md +21 -0
  71. package/skills/supabase-local-dev-loop/references/implementation.md +60 -0
  72. package/skills/supabase-migration-deep-dive/SKILL.md +338 -177
  73. package/skills/supabase-migration-deep-dive/references/backfill-versioning-rollback.md +258 -0
  74. package/skills/supabase-migration-deep-dive/references/errors.md +11 -0
  75. package/skills/supabase-migration-deep-dive/references/examples.md +12 -0
  76. package/skills/supabase-migration-deep-dive/references/implementation-plan.md +80 -0
  77. package/skills/supabase-migration-deep-dive/references/pre-migration-assessment.md +39 -0
  78. package/skills/supabase-multi-env-setup/SKILL.md +393 -152
  79. package/skills/supabase-multi-env-setup/references/configuration-structure.md +59 -0
  80. package/skills/supabase-multi-env-setup/references/errors.md +11 -0
  81. package/skills/supabase-multi-env-setup/references/examples.md +11 -0
  82. package/skills/supabase-observability/SKILL.md +318 -196
  83. package/skills/supabase-observability/references/alert-configuration.md +40 -0
  84. package/skills/supabase-observability/references/errors.md +11 -0
  85. package/skills/supabase-observability/references/examples.md +13 -0
  86. package/skills/supabase-observability/references/metrics-collection.md +65 -0
  87. package/skills/supabase-performance-tuning/SKILL.md +304 -160
  88. package/skills/supabase-performance-tuning/references/caching-strategy.md +49 -0
  89. package/skills/supabase-performance-tuning/references/errors.md +11 -0
  90. package/skills/supabase-performance-tuning/references/examples.md +13 -0
  91. package/skills/supabase-policy-guardrails/SKILL.md +248 -221
  92. package/skills/supabase-policy-guardrails/references/ci-cost-security.md +484 -0
  93. package/skills/supabase-policy-guardrails/references/errors.md +11 -0
  94. package/skills/supabase-policy-guardrails/references/eslint-rules.md +46 -0
  95. package/skills/supabase-policy-guardrails/references/examples.md +10 -0
  96. package/skills/supabase-prod-checklist/SKILL.md +474 -84
  97. package/skills/supabase-prod-checklist/references/errors.md +63 -0
  98. package/skills/supabase-prod-checklist/references/examples.md +153 -0
  99. package/skills/supabase-prod-checklist/references/implementation.md +113 -0
  100. package/skills/supabase-rate-limits/SKILL.md +311 -98
  101. package/skills/supabase-rate-limits/references/errors.md +11 -0
  102. package/skills/supabase-rate-limits/references/examples.md +46 -0
  103. package/skills/supabase-rate-limits/references/implementation.md +66 -0
  104. package/skills/supabase-reference-architecture/SKILL.md +249 -182
  105. package/skills/supabase-reference-architecture/references/errors.md +29 -0
  106. package/skills/supabase-reference-architecture/references/examples.md +116 -0
  107. package/skills/supabase-reference-architecture/references/key-components.md +244 -0
  108. package/skills/supabase-reference-architecture/references/project-structure.md +109 -0
  109. package/skills/supabase-reliability-patterns/SKILL.md +229 -234
  110. package/skills/supabase-reliability-patterns/references/circuit-breaker.md +36 -0
  111. package/skills/supabase-reliability-patterns/references/dead-letter-queue.md +48 -0
  112. package/skills/supabase-reliability-patterns/references/errors.md +11 -0
  113. package/skills/supabase-reliability-patterns/references/examples.md +11 -0
  114. package/skills/supabase-reliability-patterns/references/idempotency-keys.md +36 -0
  115. package/skills/supabase-reliability-patterns/references/offline-degradation-health-dualwrite.md +489 -0
  116. package/skills/supabase-schema-from-requirements/SKILL.md +373 -34
  117. package/skills/supabase-sdk-patterns/SKILL.md +388 -99
  118. package/skills/supabase-sdk-patterns/references/errors.md +11 -0
  119. package/skills/supabase-sdk-patterns/references/examples.md +45 -0
  120. package/skills/supabase-sdk-patterns/references/implementation.md +67 -0
  121. package/skills/supabase-security-basics/SKILL.md +282 -102
  122. package/skills/supabase-security-basics/references/errors.md +10 -0
  123. package/skills/supabase-security-basics/references/examples.md +70 -0
  124. package/skills/supabase-security-basics/references/implementation.md +39 -0
  125. package/skills/supabase-upgrade-migration/SKILL.md +248 -66
  126. package/skills/supabase-upgrade-migration/references/errors.md +10 -0
  127. package/skills/supabase-upgrade-migration/references/examples.md +51 -0
  128. package/skills/supabase-upgrade-migration/references/implementation.md +29 -0
  129. package/skills/supabase-webhooks-events/SKILL.md +412 -138
  130. package/skills/supabase-webhooks-events/references/errors.md +55 -0
  131. package/skills/supabase-webhooks-events/references/event-handler-pattern.md +106 -0
  132. package/skills/supabase-webhooks-events/references/examples.md +133 -0
  133. package/skills/supabase-webhooks-events/references/signature-verification.md +165 -0
@@ -1,238 +1,305 @@
1
1
  ---
2
2
  name: supabase-reference-architecture
3
- description: |
4
- Implement Supabase reference architecture with best-practice project layout.
5
- Use when designing new Supabase integrations, reviewing project structure,
6
- or establishing architecture standards for Supabase applications.
7
- Trigger with phrases like "supabase architecture", "supabase best practices",
8
- "supabase project structure", "how to organize supabase", "supabase layout".
9
- allowed-tools: Read, Grep
3
+ description: "Implement enterprise Supabase reference architectures \u2014 monorepo\
4
+ \ layout, multi-tenant RLS,\nmicroservices with cross-project access, framework\
5
+ \ integration, edge functions, caching,\nqueue patterns, and audit logging.\nUse\
6
+ \ when designing a new Supabase project from scratch, reviewing project structure\
7
+ \ for\nproduction readiness, planning multi-tenant isolation, or establishing team\
8
+ \ architecture standards.\nTrigger with phrases like \"supabase architecture\",\
9
+ \ \"supabase project structure\",\n\"supabase monorepo\", \"supabase multi-tenant\"\
10
+ , \"supabase reference design\",\n\"how to organize supabase at scale\".\n"
11
+ allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(npx:*), Bash(supabase:*), Grep,
12
+ Glob
10
13
  version: 1.0.0
11
14
  license: MIT
12
15
  author: Jeremy Longshore <jeremy@intentsolutions.io>
16
+ tags:
17
+ - saas
18
+ - supabase
19
+ - architecture
20
+ - patterns
21
+ - multi-tenant
22
+ - monorepo
23
+ compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw
13
24
  ---
14
-
15
25
  # Supabase Reference Architecture
16
26
 
17
27
  ## Overview
18
- Production-ready architecture patterns for Supabase integrations.
19
28
 
20
- ## Prerequisites
21
- - Understanding of layered architecture
22
- - Supabase SDK knowledge
23
- - TypeScript project setup
24
- - Testing framework configured
29
+ Production Supabase applications need more than a flat `lib/supabase.ts` file. This skill covers five enterprise architecture patterns: monorepo with shared types, multi-tenant RLS isolation, microservices with separate Supabase projects, framework integration (Next.js / SvelteKit), and operational patterns (edge functions, caching, queues, audit trails). Each pattern stands alone — pick the ones that match your scale.
25
30
 
26
- ## Project Structure
31
+ For the full monorepo directory layout and microservices cross-project access, see [Project Structure](references/project-structure.md). For edge functions, caching, queue, and audit trail patterns, see [Operational Patterns](references/key-components.md).
27
32
 
28
- ```
29
- my-supabase-project/
30
- ├── src/
31
- │ ├── supabase/
32
- │ │ ├── client.ts # Singleton client wrapper
33
- │ │ ├── config.ts # Environment configuration
34
- │ │ ├── types.ts # TypeScript types
35
- │ │ ├── errors.ts # Custom error classes
36
- │ │ └── handlers/
37
- │ │ ├── webhooks.ts # Webhook handlers
38
- │ │ └── events.ts # Event processing
39
- │ ├── services/
40
- │ │ └── supabase/
41
- │ │ ├── index.ts # Service facade
42
- │ │ ├── sync.ts # Data synchronization
43
- │ │ └── cache.ts # Caching layer
44
- │ ├── api/
45
- │ │ └── supabase/
46
- │ │ └── webhook.ts # Webhook endpoint
47
- │ └── jobs/
48
- │ └── supabase/
49
- │ └── sync.ts # Background sync job
50
- ├── tests/
51
- │ ├── unit/
52
- │ │ └── supabase/
53
- │ └── integration/
54
- │ └── supabase/
55
- ├── config/
56
- │ ├── supabase.development.json
57
- │ ├── supabase.staging.json
58
- │ └── supabase.production.json
59
- └── docs/
60
- └── supabase/
61
- ├── SETUP.md
62
- └── RUNBOOK.md
63
- ```
33
+ ## Prerequisites
64
34
 
65
- ## Layer Architecture
35
+ - `@supabase/supabase-js` v2+ installed (`npm install @supabase/supabase-js`)
36
+ - Supabase CLI installed (`npm install -g supabase`)
37
+ - A Supabase project at [supabase.com/dashboard](https://supabase.com/dashboard)
38
+ - Familiarity with `supabase-install-auth` (project URL, anon key, service role key)
39
+ - PostgreSQL basics (RLS policies, triggers, functions)
66
40
 
67
- ```
68
- ┌─────────────────────────────────────────┐
69
- │ API Layer │
70
- │ (Controllers, Routes, Webhooks) │
71
- ├─────────────────────────────────────────┤
72
- │ Service Layer │
73
- │ (Business Logic, Orchestration) │
74
- ├─────────────────────────────────────────┤
75
- │ Supabase Layer │
76
- │ (Client, Types, Error Handling) │
77
- ├─────────────────────────────────────────┤
78
- │ Infrastructure Layer │
79
- │ (Cache, Queue, Monitoring) │
80
- └─────────────────────────────────────────┘
81
- ```
41
+ ## Instructions
42
+
43
+ ### Step 1: Client Singleton — The Foundation
82
44
 
83
- ## Key Components
45
+ Every app in the monorepo imports from a shared package instead of creating its own client. This guarantees a single source of truth for the URL, keys, and type definitions.
84
46
 
85
- ### Step 1: Client Wrapper
86
47
  ```typescript
87
- // src/supabase/client.ts
88
- export class SupabaseService {
89
- private client: SupabaseClient;
90
- private cache: Cache;
91
- private monitor: Monitor;
92
-
93
- constructor(config: SupabaseConfig) {
94
- this.client = new SupabaseClient(config);
95
- this.cache = new Cache(config.cacheOptions);
96
- this.monitor = new Monitor('supabase');
97
- }
48
+ // packages/supabase/src/client.ts
49
+ import { createClient, SupabaseClient } from '@supabase/supabase-js'
50
+ import type { Database } from './database.types'
98
51
 
99
- async get(id: string): Promise<Resource> {
100
- return this.cache.getOrFetch(id, () =>
101
- this.monitor.track('get', () => this.client.get(id))
102
- );
103
- }
104
- }
105
- ```
52
+ let client: SupabaseClient<Database> | null = null
106
53
 
107
- ### Step 2: Error Boundary
108
- ```typescript
109
- // src/supabase/errors.ts
110
- export class SupabaseServiceError extends Error {
111
- constructor(
112
- message: string,
113
- public readonly code: string,
114
- public readonly retryable: boolean,
115
- public readonly originalError?: Error
116
- ) {
117
- super(message);
118
- this.name = 'SupabaseServiceError';
54
+ export function getSupabaseClient(): SupabaseClient<Database> {
55
+ if (!client) {
56
+ const url = process.env.NEXT_PUBLIC_SUPABASE_URL ?? process.env.SUPABASE_URL
57
+ const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ?? process.env.SUPABASE_ANON_KEY
58
+ if (!url || !key) {
59
+ throw new Error('Missing SUPABASE_URL or SUPABASE_ANON_KEY environment variables')
60
+ }
61
+ client = createClient<Database>(url, key)
119
62
  }
63
+ return client
120
64
  }
121
65
 
122
- export function wrapSupabaseError(error: unknown): SupabaseServiceError {
123
- // Transform SDK errors to application errors
66
+ // Reset for testing
67
+ export function resetClient(): void {
68
+ client = null
124
69
  }
125
70
  ```
126
71
 
127
- ### Step 3: Health Check
128
72
  ```typescript
129
- // src/supabase/health.ts
130
- export async function checkSupabaseHealth(): Promise<HealthStatus> {
131
- try {
132
- const start = Date.now();
133
- await supabaseClient.ping();
134
- return {
135
- status: 'healthy',
136
- latencyMs: Date.now() - start,
137
- };
138
- } catch (error) {
139
- return { status: 'unhealthy', error: error.message };
73
+ // packages/supabase/src/admin.ts — Server-side only, never bundle in client code
74
+ import { createClient } from '@supabase/supabase-js'
75
+ import type { Database } from './database.types'
76
+
77
+ export function getSupabaseAdmin() {
78
+ const url = process.env.SUPABASE_URL
79
+ const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY
80
+ if (!url || !serviceKey) {
81
+ throw new Error('Missing SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY — server-only')
140
82
  }
83
+ return createClient<Database>(url, serviceKey, {
84
+ auth: { autoRefreshToken: false, persistSession: false }
85
+ })
141
86
  }
142
87
  ```
143
88
 
144
- ## Data Flow Diagram
89
+ Key detail: The admin client sets `autoRefreshToken: false` and `persistSession: false` because server-side code should never store user sessions.
90
+
91
+ ### Step 2: Multi-Tenant RLS via JWT Claims
92
+
93
+ The most scalable Supabase multi-tenant pattern uses a custom JWT claim (`org_id`) combined with RLS policies. Every table includes an `org_id` column, and RLS extracts the tenant from the user's JWT — no application-level filtering needed.
94
+
95
+ ```sql
96
+ -- Migration: 20260101000000_create_tenants.sql
97
+
98
+ -- Tenants table
99
+ create table public.tenants (
100
+ id uuid primary key default gen_random_uuid(),
101
+ name text not null,
102
+ slug text unique not null,
103
+ plan text default 'free' check (plan in ('free', 'pro', 'enterprise')),
104
+ created_at timestamptz default now()
105
+ );
106
+
107
+ -- Tenant membership
108
+ create table public.tenant_members (
109
+ tenant_id uuid references public.tenants(id) on delete cascade,
110
+ user_id uuid references auth.users(id) on delete cascade,
111
+ role text default 'member' check (role in ('owner', 'admin', 'member', 'viewer')),
112
+ primary key (tenant_id, user_id)
113
+ );
145
114
 
115
+ -- Example tenant-scoped table
116
+ create table public.projects (
117
+ id uuid primary key default gen_random_uuid(),
118
+ org_id uuid not null references public.tenants(id) on delete cascade,
119
+ name text not null,
120
+ created_by uuid references auth.users(id),
121
+ created_at timestamptz default now()
122
+ );
123
+
124
+ -- Enable RLS on all tenant-scoped tables
125
+ alter table public.projects enable row level security;
126
+
127
+ -- RLS policy: users can only see rows belonging to their tenant
128
+ -- The org_id is extracted from the JWT claims set during authentication
129
+ create policy "Tenant isolation" on public.projects
130
+ for all
131
+ using (
132
+ org_id = (auth.jwt() ->> 'org_id')::uuid
133
+ );
146
134
  ```
147
- User Request
148
-
149
-
150
- ┌─────────────┐
151
- │ API │
152
- │ Gateway │
153
- └──────┬──────┘
154
-
155
-
156
- ┌─────────────┐ ┌─────────────┐
157
- │ Service │───▶│ Cache │
158
- │ Layer │ │ (Redis) │
159
- └──────┬──────┘ └─────────────┘
160
-
161
-
162
- ┌─────────────┐
163
- │ Supabase │
164
- │ Client │
165
- └──────┬──────┘
166
-
167
-
168
- ┌─────────────┐
169
- │ Supabase │
170
- │ API │
171
- └─────────────┘
135
+
136
+ The tenant-switching function verifies membership before updating the JWT claim:
137
+
138
+ ```sql
139
+ -- Helper function to set org_id in JWT claims after login
140
+ create or replace function public.set_tenant_claim(tenant_id uuid)
141
+ returns void as $$
142
+ begin
143
+ -- Verify user is a member of this tenant
144
+ if not exists (
145
+ select 1 from public.tenant_members
146
+ where tenant_members.tenant_id = set_tenant_claim.tenant_id
147
+ and tenant_members.user_id = auth.uid()
148
+ ) then
149
+ raise exception 'Not a member of tenant %', tenant_id;
150
+ end if;
151
+
152
+ -- Set the custom claim
153
+ perform auth.update_user_metadata(
154
+ auth.uid(),
155
+ jsonb_build_object('org_id', tenant_id)
156
+ );
157
+ end;
158
+ $$ language plpgsql security definer;
172
159
  ```
173
160
 
174
- ## Configuration Management
161
+ Key details for multi-tenant RLS:
162
+
163
+ - `auth.jwt() ->> 'org_id'` reads a custom claim from the user's JWT — zero application code needed
164
+ - Every tenant-scoped table must have an `org_id` column and RLS enabled
165
+ - Tenant switching requires updating the JWT claim and re-authenticating
166
+ - For row-level tenant + role permissions, combine `org_id` with a role lookup
167
+
168
+ ### Step 3: Framework Integration (Next.js)
169
+
170
+ Server components use the `service_role` key for direct database access. Client components use the `anon` key with RLS protection.
175
171
 
176
172
  ```typescript
177
- // config/supabase.ts
178
- export interface SupabaseConfig {
179
- apiKey: string;
180
- environment: 'development' | 'staging' | 'production';
181
- timeout: number;
182
- retries: number;
183
- cache: {
184
- enabled: boolean;
185
- ttlSeconds: number;
186
- };
173
+ // app/lib/supabase-server.ts — Next.js App Router (server components)
174
+ import { createClient } from '@supabase/supabase-js'
175
+ import { cookies } from 'next/headers'
176
+ import type { Database } from '@my-platform/supabase'
177
+
178
+ export async function getSupabaseServer() {
179
+ const cookieStore = await cookies()
180
+
181
+ return createClient<Database>(
182
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
183
+ process.env.SUPABASE_SERVICE_ROLE_KEY!,
184
+ {
185
+ auth: { autoRefreshToken: false, persistSession: false },
186
+ global: {
187
+ headers: {
188
+ // Forward the user's auth cookie for RLS context
189
+ cookie: cookieStore.toString()
190
+ }
191
+ }
192
+ }
193
+ )
187
194
  }
188
195
 
189
- export function loadSupabaseConfig(): SupabaseConfig {
190
- const env = process.env.NODE_ENV || 'development';
191
- return require(`./supabase.${env}.json`);
196
+ // app/projects/page.tsx Server component with direct DB access
197
+ export default async function ProjectsPage() {
198
+ const supabase = await getSupabaseServer()
199
+ const { data: projects } = await supabase
200
+ .from('projects')
201
+ .select('id, name, created_at')
202
+ .order('created_at', { ascending: false })
203
+ .limit(50)
204
+
205
+ return <ProjectList projects={projects ?? []} />
192
206
  }
193
207
  ```
194
208
 
195
- ## Instructions
196
-
197
- ### Step 1: Create Directory Structure
198
- Set up the project layout following the reference structure above.
209
+ ```typescript
210
+ // app/lib/supabase-browser.ts — Client components use the anon key
211
+ 'use client'
212
+ import { createClient } from '@supabase/supabase-js'
213
+ import type { Database } from '@my-platform/supabase'
199
214
 
200
- ### Step 2: Implement Client Wrapper
201
- Create the singleton client with caching and monitoring.
215
+ let browserClient: ReturnType<typeof createClient<Database>> | null = null
202
216
 
203
- ### Step 3: Add Error Handling
204
- Implement custom error classes for Supabase operations.
217
+ export function getSupabaseBrowser() {
218
+ if (!browserClient) {
219
+ browserClient = createClient<Database>(
220
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
221
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
222
+ )
223
+ }
224
+ return browserClient
225
+ }
226
+ ```
205
227
 
206
- ### Step 4: Configure Health Checks
207
- Add health check endpoint for Supabase connectivity.
228
+ For SvelteKit integration and additional framework patterns, see [Examples](references/examples.md).
208
229
 
209
230
  ## Output
210
- - Structured project layout
211
- - Client wrapper with caching
212
- - Error boundary implemented
213
- - Health checks configured
231
+
232
+ After applying these patterns you will have:
233
+
234
+ - Monorepo with shared Supabase client, typed database access, and centralized migrations
235
+ - Multi-tenant RLS isolation using `auth.jwt() ->> 'org_id'` — zero application-level filtering
236
+ - Framework-specific integration for Next.js (server/client split) and SvelteKit (hooks)
237
+ - Edge Functions, caching layer, job queue, and audit trail (see [Operational Patterns](references/key-components.md))
214
238
 
215
239
  ## Error Handling
216
- | Issue | Cause | Solution |
240
+
241
+ | Error | Cause | Solution |
217
242
  |-------|-------|----------|
218
- | Circular dependencies | Wrong layering | Separate concerns by layer |
219
- | Config not loading | Wrong paths | Verify config file locations |
220
- | Type errors | Missing types | Add Supabase types |
221
- | Test isolation | Shared state | Use dependency injection |
243
+ | `Missing SUPABASE_URL or SUPABASE_ANON_KEY` | Environment variables not set | Check `.env` file and ensure variables are loaded |
244
+ | `new row violates row-level security policy` | RLS blocks the operation | Verify `org_id` JWT claim matches the row's `org_id` |
245
+ | `Not a member of tenant` | User tried switching to unauthorized tenant | Check `tenant_members` table for the user-tenant pair |
246
+ | `TypeError: Cannot read properties of null` | Client singleton not initialized | Ensure env vars are available before first `getSupabaseClient()` call |
247
+ | `cron.schedule: permission denied` | `pg_cron` extension not enabled | Enable via dashboard: Database > Extensions > pg_cron |
248
+
249
+ For the full error reference including RLS debugging and cross-project troubleshooting, see [Error Handling Reference](references/errors.md).
222
250
 
223
251
  ## Examples
224
252
 
225
- ### Quick Setup Script
226
- ```bash
227
- # Create reference structure
228
- mkdir -p src/supabase/{handlers} src/services/supabase src/api/supabase
229
- touch src/supabase/{client,config,types,errors}.ts
230
- touch src/services/supabase/{index,sync,cache}.ts
253
+ ### Multi-Tenant Query Flow (TypeScript)
254
+
255
+ ```typescript
256
+ import { createClient } from '@supabase/supabase-js'
257
+ import type { Database } from './database.types'
258
+
259
+ const supabase = createClient<Database>(
260
+ process.env.SUPABASE_URL!,
261
+ process.env.SUPABASE_ANON_KEY!
262
+ )
263
+
264
+ // 1. Sign in
265
+ const { data: { session } } = await supabase.auth.signInWithPassword({
266
+ email: 'user@example.com',
267
+ password: 'secure-password'
268
+ })
269
+
270
+ // 2. Switch tenant context
271
+ const { error: claimError } = await supabase.rpc('set_tenant_claim', {
272
+ tenant_id: 'tenant-uuid-here'
273
+ })
274
+ if (claimError) throw claimError
275
+
276
+ // 3. Refresh session to pick up new JWT claims
277
+ await supabase.auth.refreshSession()
278
+
279
+ // 4. All subsequent queries are automatically scoped to this tenant
280
+ const { data: projects } = await supabase
281
+ .from('projects')
282
+ .select('id, name, created_at')
283
+ .order('created_at', { ascending: false })
284
+
285
+ console.log('Tenant projects:', projects)
286
+ // Only returns projects where org_id matches the JWT claim
231
287
  ```
232
288
 
289
+ For the job queue consumer example and SvelteKit integration, see [Examples](references/examples.md).
290
+
233
291
  ## Resources
234
- - [Supabase SDK Documentation](https://supabase.com/docs/sdk)
235
- - [Supabase Best Practices](https://supabase.com/docs/best-practices)
236
292
 
237
- ## Flagship Skills
238
- For multi-environment setup, see `supabase-multi-env-setup`.
293
+ - [Supabase Architecture](https://supabase.com/docs/guides/getting-started/architecture)
294
+ - [Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security)
295
+ - [Multi-Tenant RLS](https://supabase.com/docs/guides/auth/row-level-security#multi-tenant-applications)
296
+ - [Edge Functions](https://supabase.com/docs/guides/functions)
297
+ - [TypeScript Support](https://supabase.com/docs/reference/javascript/typescript-support)
298
+ - [Generating Types](https://supabase.com/docs/guides/api/rest/generating-types)
299
+ - [pg_cron Extension](https://supabase.com/docs/guides/database/extensions/pg_cron)
300
+ - [Auth JWT Helper](https://supabase.com/docs/guides/auth/jwts)
301
+ - [createClient Reference](https://supabase.com/docs/reference/javascript/initializing)
302
+
303
+ ## Next Steps
304
+
305
+ For performance optimization and indexing strategies, see `supabase-performance-tuning`. For deployment pipelines and CI integration, see `supabase-ci-integration`. For security hardening and policy guardrails, see `supabase-security-basics`.
@@ -0,0 +1,29 @@
1
+ # Error Handling Reference
2
+
3
+ ## Common Errors
4
+
5
+ | Error | Cause | Solution |
6
+ |-------|-------|----------|
7
+ | `Missing SUPABASE_URL or SUPABASE_ANON_KEY` | Environment variables not set | Check `.env` file and ensure variables are loaded |
8
+ | `new row violates row-level security policy` | RLS blocks the operation | Verify `org_id` JWT claim matches the row's `org_id` |
9
+ | `Not a member of tenant` | User tried switching to unauthorized tenant | Check `tenant_members` table for the user-tenant pair |
10
+ | `relation "public.audit_log" does not exist` | Audit migration not applied | Run `supabase db push` or `supabase db reset` |
11
+ | `permission denied for function claim_next_job` | Missing execute grant | Run `grant execute on function claim_next_job to authenticated` |
12
+ | `Cross-project profile lookup failed` | Wrong service_role key for the target project | Verify `MAIN_SUPABASE_SERVICE_ROLE_KEY` matches the main project |
13
+ | `TypeError: Cannot read properties of null` | Client singleton not initialized | Ensure env vars are available before first `getSupabaseClient()` call |
14
+ | `cron.schedule: permission denied` | `pg_cron` extension not enabled | Enable via dashboard: Database > Extensions > pg_cron |
15
+
16
+ ## RLS Debugging Checklist
17
+
18
+ 1. Verify `alter table ... enable row level security` was run on the table
19
+ 2. Check that at least one permissive policy exists for the operation (SELECT, INSERT, UPDATE, DELETE)
20
+ 3. Confirm the JWT contains the expected `org_id` claim: `select auth.jwt() ->> 'org_id'`
21
+ 4. Test the policy logic directly: `select * from projects where org_id = 'expected-uuid'`
22
+ 5. Use `supabase inspect db policies` to list all active policies
23
+
24
+ ## Cross-Project Access Troubleshooting
25
+
26
+ - **401 Unauthorized**: The `service_role` key is wrong or belongs to a different project
27
+ - **Network timeout**: Cross-region calls add latency; consider caching or co-locating projects
28
+ - **Rate limited**: Supabase enforces per-project rate limits; distribute load across projects
29
+ - **Type mismatch**: Regenerate types for both projects after schema changes
@@ -0,0 +1,116 @@
1
+ # Examples — Multi-Tenant Setup and Job Queue Consumer
2
+
3
+ ## Complete Multi-Tenant Setup (TypeScript)
4
+
5
+ ```typescript
6
+ import { createClient } from '@supabase/supabase-js'
7
+ import type { Database } from './database.types'
8
+
9
+ const supabase = createClient<Database>(
10
+ process.env.SUPABASE_URL!,
11
+ process.env.SUPABASE_ANON_KEY!
12
+ )
13
+
14
+ // 1. Sign in
15
+ const { data: { session } } = await supabase.auth.signInWithPassword({
16
+ email: 'user@example.com',
17
+ password: 'secure-password'
18
+ })
19
+
20
+ // 2. Switch tenant context
21
+ const { error: claimError } = await supabase.rpc('set_tenant_claim', {
22
+ tenant_id: 'tenant-uuid-here'
23
+ })
24
+ if (claimError) throw claimError
25
+
26
+ // 3. Refresh session to pick up new JWT claims
27
+ await supabase.auth.refreshSession()
28
+
29
+ // 4. All subsequent queries are automatically scoped to this tenant
30
+ const { data: projects } = await supabase
31
+ .from('projects')
32
+ .select('id, name, created_at')
33
+ .order('created_at', { ascending: false })
34
+
35
+ console.log('Tenant projects:', projects)
36
+ // Only returns projects where org_id matches the JWT claim
37
+ ```
38
+
39
+ ## Job Queue Consumer (TypeScript)
40
+
41
+ ```typescript
42
+ import { getSupabaseAdmin } from './admin'
43
+
44
+ async function processJobs() {
45
+ const supabase = getSupabaseAdmin()
46
+
47
+ while (true) {
48
+ // Atomically claim the next job
49
+ const { data: job, error } = await supabase
50
+ .rpc('claim_next_job', { p_job_type: 'send-email' })
51
+
52
+ if (error || !job) {
53
+ // No jobs available — wait before polling again
54
+ await new Promise(r => setTimeout(r, 5000))
55
+ continue
56
+ }
57
+
58
+ try {
59
+ // Process the job
60
+ console.log(`Processing job ${job.id}:`, job.payload)
61
+ await sendEmail(job.payload)
62
+
63
+ // Mark complete
64
+ await supabase
65
+ .from('job_queue')
66
+ .update({ status: 'completed', completed_at: new Date().toISOString() })
67
+ .eq('id', job.id)
68
+ } catch (err) {
69
+ // Mark failed — pg_cron will retry if attempts < max_attempts
70
+ await supabase
71
+ .from('job_queue')
72
+ .update({
73
+ status: 'failed',
74
+ error_message: err instanceof Error ? err.message : String(err)
75
+ })
76
+ .eq('id', job.id)
77
+ }
78
+ }
79
+ }
80
+
81
+ async function sendEmail(payload: Record<string, unknown>) {
82
+ console.log('Sending email to:', payload.to)
83
+ }
84
+
85
+ processJobs().catch(console.error)
86
+ ```
87
+
88
+ ## SvelteKit Integration
89
+
90
+ ```typescript
91
+ // src/hooks.server.ts — SvelteKit server hooks
92
+ import { createClient } from '@supabase/supabase-js'
93
+ import type { Handle } from '@sveltejs/kit'
94
+ import type { Database } from '@my-platform/supabase'
95
+
96
+ export const handle: Handle = async ({ event, resolve }) => {
97
+ event.locals.supabase = createClient<Database>(
98
+ import.meta.env.VITE_SUPABASE_URL,
99
+ import.meta.env.VITE_SUPABASE_ANON_KEY,
100
+ {
101
+ global: {
102
+ headers: { cookie: event.request.headers.get('cookie') ?? '' }
103
+ }
104
+ }
105
+ )
106
+
107
+ // Server-side admin client for privileged operations
108
+ event.locals.supabaseAdmin = createClient<Database>(
109
+ import.meta.env.VITE_SUPABASE_URL,
110
+ import.meta.env.SUPABASE_SERVICE_ROLE_KEY,
111
+ { auth: { autoRefreshToken: false, persistSession: false } }
112
+ )
113
+
114
+ return resolve(event)
115
+ }
116
+ ```