@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,284 +1,463 @@
1
1
  ---
2
2
  name: supabase-architecture-variants
3
- description: |
4
- Choose and implement Supabase validated architecture blueprints for different scales.
5
- Use when designing new Supabase integrations, choosing between monolith/service/microservice
6
- architectures, or planning migration paths for Supabase applications.
7
- Trigger with phrases like "supabase architecture", "supabase blueprint",
8
- "how to structure supabase", "supabase project layout", "supabase microservice".
9
- allowed-tools: Read, Grep
3
+ description: 'Implement Supabase across different app architectures: Next.js SSR with
4
+
5
+ server components using service_role and client components with anon key,
6
+
7
+ SPA (React/Vue), mobile (React Native), serverless (Edge Functions),
8
+
9
+ and multi-tenant with schema-per-tenant or RLS isolation.
10
+
11
+ Use when choosing how to integrate Supabase into your specific stack,
12
+
13
+ setting up SSR auth flows, configuring mobile deep links,
14
+
15
+ or designing multi-tenant data isolation.
16
+
17
+ Trigger with phrases like "supabase next.js", "supabase SSR",
18
+
19
+ "supabase react native", "supabase SPA", "supabase serverless",
20
+
21
+ "supabase multi-tenant", "supabase server component",
22
+
23
+ "supabase architecture", "supabase service_role server".
24
+
25
+ '
26
+ allowed-tools: Read, Write, Edit, Bash(supabase:*), Bash(npx:*), Grep
10
27
  version: 1.0.0
11
28
  license: MIT
12
29
  author: Jeremy Longshore <jeremy@intentsolutions.io>
30
+ tags:
31
+ - saas
32
+ - supabase
33
+ - architecture
34
+ - nextjs
35
+ - ssr
36
+ - spa
37
+ - mobile
38
+ - multi-tenant
39
+ - serverless
40
+ compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw
13
41
  ---
14
-
15
42
  # Supabase Architecture Variants
16
43
 
17
44
  ## Overview
18
- Three validated architecture blueprints for Supabase integrations.
45
+
46
+ Different application architectures require fundamentally different Supabase `createClient` configurations. The critical distinction is **where the client runs** (browser vs server) and **which key it uses** (anon key respects RLS; service_role bypasses it). This skill provides production-ready patterns for five architectures: **Next.js SSR** (server components with service_role, client components with anon), **SPA** (React/Vue with browser-only client), **Mobile** (React Native with deep link auth), **Serverless** (Edge Functions with per-request clients), and **Multi-tenant** (RLS-based or schema-per-tenant isolation).
19
47
 
20
48
  ## Prerequisites
21
- - Understanding of team size and DAU requirements
22
- - Knowledge of deployment infrastructure
23
- - Clear SLA requirements
24
- - Growth projections available
25
49
 
26
- ## Variant A: Monolith (Simple)
50
+ - `@supabase/supabase-js` v2+ installed
51
+ - `@supabase/ssr` package for Next.js SSR (v0.5+)
52
+ - Supabase project with URL, anon key, and service role key
53
+ - TypeScript project with generated database types (`supabase gen types typescript`)
54
+ - For mobile: React Native with Expo or bare workflow
27
55
 
28
- **Best for:** MVPs, small teams, < 10K daily active users
56
+ ## Step 1 Next.js SSR (App Router with Server and Client Components)
29
57
 
30
- ```
31
- my-app/
32
- ├── src/
33
- │ ├── supabase/
34
- │ │ ├── client.ts # Singleton client
35
- │ │ ├── types.ts # Types
36
- │ │ └── middleware.ts # Express middleware
37
- │ ├── routes/
38
- │ │ └── api/
39
- │ │ └── supabase.ts # API routes
40
- │ └── index.ts
41
- ├── tests/
42
- │ └── supabase.test.ts
43
- └── package.json
44
- ```
58
+ Next.js App Router requires **two separate clients**: a server-side client using cookies for auth (with `@supabase/ssr`) and a browser client for client components. Never expose `service_role` to the client.
45
59
 
46
- ### Key Characteristics
47
- - Single deployment unit
48
- - Synchronous Supabase calls in request path
49
- - In-memory caching
50
- - Simple error handling
60
+ ### Server-Side Client (for Server Components, Route Handlers, Server Actions)
51
61
 
52
- ### Code Pattern
53
62
  ```typescript
54
- // Direct integration in route handler
55
- app.post('/api/create', async (req, res) => {
56
- try {
57
- const result = await supabaseClient.create(req.body);
58
- res.json(result);
59
- } catch (error) {
60
- res.status(500).json({ error: error.message });
61
- }
62
- });
63
+ // lib/supabase/server.ts
64
+ import { createServerClient } from '@supabase/ssr'
65
+ import { cookies } from 'next/headers'
66
+ import type { Database } from '../database.types'
67
+
68
+ export async function createSupabaseServer() {
69
+ const cookieStore = await cookies()
70
+
71
+ return createServerClient<Database>(
72
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
73
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
74
+ {
75
+ cookies: {
76
+ getAll() {
77
+ return cookieStore.getAll()
78
+ },
79
+ setAll(cookiesToSet) {
80
+ try {
81
+ cookiesToSet.forEach(({ name, value, options }) =>
82
+ cookieStore.set(name, value, options)
83
+ )
84
+ } catch {
85
+ // Called from Server Component — cookies are read-only
86
+ }
87
+ },
88
+ },
89
+ }
90
+ )
91
+ }
92
+
93
+ // Admin client for server-only operations (bypasses RLS)
94
+ // NEVER import this in client components or expose to the browser
95
+ import { createClient } from '@supabase/supabase-js'
96
+
97
+ export function createSupabaseAdmin() {
98
+ return createClient<Database>(
99
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
100
+ process.env.SUPABASE_SERVICE_ROLE_KEY!, // NOT NEXT_PUBLIC_ — server only
101
+ {
102
+ auth: { autoRefreshToken: false, persistSession: false },
103
+ }
104
+ )
105
+ }
63
106
  ```
64
107
 
65
- ---
108
+ ### Client-Side Client (for Client Components)
109
+
110
+ ```typescript
111
+ // lib/supabase/client.ts
112
+ 'use client'
113
+
114
+ import { createBrowserClient } from '@supabase/ssr'
115
+ import type { Database } from '../database.types'
116
+
117
+ let client: ReturnType<typeof createBrowserClient<Database>> | null = null
66
118
 
67
- ## Variant B: Service Layer (Moderate)
119
+ export function createSupabaseBrowser() {
120
+ if (client) return client
68
121
 
69
- **Best for:** Growing startups, 10K-100K DAU, multiple integrations
122
+ client = createBrowserClient<Database>(
123
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
124
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! // anon key only — respects RLS
125
+ )
70
126
 
127
+ return client
128
+ }
71
129
  ```
72
- my-app/
73
- ├── src/
74
- │ ├── services/
75
- │ │ ├── supabase/
76
- │ │ │ ├── client.ts # Client wrapper
77
- │ │ │ ├── service.ts # Business logic
78
- │ │ │ ├── repository.ts # Data access
79
- │ │ │ └── types.ts
80
- │ │ └── index.ts # Service exports
81
- │ ├── controllers/
82
- │ │ └── supabase.ts
83
- │ ├── routes/
84
- │ ├── middleware/
85
- │ ├── queue/
86
- │ │ └── supabase-processor.ts # Async processing
87
- │ └── index.ts
88
- ├── config/
89
- │ └── supabase/
90
- └── package.json
130
+
131
+ ### Middleware for Auth Session Refresh
132
+
133
+ ```typescript
134
+ // middleware.ts
135
+ import { createServerClient } from '@supabase/ssr'
136
+ import { NextResponse, type NextRequest } from 'next/server'
137
+
138
+ export async function middleware(request: NextRequest) {
139
+ let response = NextResponse.next({ request })
140
+
141
+ const supabase = createServerClient(
142
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
143
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
144
+ {
145
+ cookies: {
146
+ getAll() {
147
+ return request.cookies.getAll()
148
+ },
149
+ setAll(cookiesToSet) {
150
+ cookiesToSet.forEach(({ name, value }) =>
151
+ request.cookies.set(name, value)
152
+ )
153
+ response = NextResponse.next({ request })
154
+ cookiesToSet.forEach(({ name, value, options }) =>
155
+ response.cookies.set(name, value, options)
156
+ )
157
+ },
158
+ },
159
+ }
160
+ )
161
+
162
+ // Refresh session — this is the critical call
163
+ await supabase.auth.getUser()
164
+
165
+ return response
166
+ }
167
+
168
+ export const config = {
169
+ matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
170
+ }
91
171
  ```
92
172
 
93
- ### Key Characteristics
94
- - Separation of concerns
95
- - Background job processing
96
- - Redis caching
97
- - Circuit breaker pattern
98
- - Structured error handling
173
+ ### Server Component Usage
99
174
 
100
- ### Code Pattern
101
175
  ```typescript
102
- // Service layer abstraction
103
- class SupabaseService {
104
- constructor(
105
- private client: SupabaseClient,
106
- private cache: CacheService,
107
- private queue: QueueService
108
- ) {}
109
-
110
- async createResource(data: CreateInput): Promise<Resource> {
111
- // Business logic before API call
112
- const validated = this.validate(data);
113
-
114
- // Check cache
115
- const cached = await this.cache.get(cacheKey);
116
- if (cached) return cached;
117
-
118
- // API call with retry
119
- const result = await this.withRetry(() =>
120
- this.client.create(validated)
121
- );
122
-
123
- // Cache result
124
- await this.cache.set(cacheKey, result, 300);
125
-
126
- // Async follow-up
127
- await this.queue.enqueue('supabase.post-create', result);
128
-
129
- return result;
130
- }
176
+ // app/dashboard/page.tsx
177
+ import { createSupabaseServer } from '@/lib/supabase/server'
178
+ import { redirect } from 'next/navigation'
179
+
180
+ export default async function DashboardPage() {
181
+ const supabase = await createSupabaseServer()
182
+
183
+ const { data: { user } } = await supabase.auth.getUser()
184
+ if (!user) redirect('/login')
185
+
186
+ const { data: projects, error } = await supabase
187
+ .from('projects')
188
+ .select('id, name, status, created_at')
189
+ .eq('user_id', user.id)
190
+ .order('created_at', { ascending: false })
191
+
192
+ if (error) throw new Error(`Failed to load projects: ${error.message}`)
193
+
194
+ return (
195
+ <div>
196
+ <h1>My Projects</h1>
197
+ {projects.map(p => <ProjectCard key={p.id} project={p} />)}
198
+ </div>
199
+ )
131
200
  }
132
201
  ```
133
202
 
134
- ---
203
+ ### Server Action with Admin Client
135
204
 
136
- ## Variant C: Microservice (Complex)
205
+ ```typescript
206
+ // app/actions/admin.ts
207
+ 'use server'
137
208
 
138
- **Best for:** Enterprise, 100K+ DAU, strict SLAs
209
+ import { createSupabaseAdmin } from '@/lib/supabase/server'
139
210
 
140
- ```
141
- supabase-service/ # Dedicated microservice
142
- ├── src/
143
- │ ├── api/
144
- │ │ ├── grpc/
145
- │ │ │ └── supabase.proto
146
- │ │ └── rest/
147
- │ │ └── routes.ts
148
- │ ├── domain/
149
- │ │ ├── entities/
150
- │ │ ├── events/
151
- │ │ └── services/
152
- │ ├── infrastructure/
153
- │ │ ├── supabase/
154
- │ │ │ ├── client.ts
155
- │ │ │ ├── mapper.ts
156
- │ │ │ └── circuit-breaker.ts
157
- │ │ ├── cache/
158
- │ │ ├── queue/
159
- │ │ └── database/
160
- │ └── index.ts
161
- ├── config/
162
- ├── k8s/
163
- │ ├── deployment.yaml
164
- │ ├── service.yaml
165
- │ └── hpa.yaml
166
- └── package.json
167
-
168
- other-services/
169
- ├── order-service/ # Calls supabase-service
170
- ├── payment-service/
171
- └── notification-service/
211
+ export async function deleteUserAccount(userId: string) {
212
+ const supabase = createSupabaseAdmin()
213
+
214
+ // Admin operation — bypasses RLS
215
+ const { error: deleteError } = await supabase
216
+ .from('user_data')
217
+ .delete()
218
+ .eq('user_id', userId)
219
+
220
+ if (deleteError) throw new Error(`Data deletion failed: ${deleteError.message}`)
221
+
222
+ // Delete auth user
223
+ const { error: authError } = await supabase.auth.admin.deleteUser(userId)
224
+ if (authError) throw new Error(`Auth deletion failed: ${authError.message}`)
225
+ }
172
226
  ```
173
227
 
174
- ### Key Characteristics
175
- - Dedicated Supabase microservice
176
- - gRPC for internal communication
177
- - Event-driven architecture
178
- - Database per service
179
- - Kubernetes autoscaling
180
- - Distributed tracing
181
- - Circuit breaker per service
228
+ ## Step 2 — SPA (React/Vue) and Mobile (React Native)
182
229
 
183
- ### Code Pattern
184
- ```typescript
185
- // Event-driven with domain isolation
186
- class SupabaseAggregate {
187
- private events: DomainEvent[] = [];
230
+ ### SPA Architecture (React with Vite)
188
231
 
189
- process(command: SupabaseCommand): void {
190
- // Domain logic
191
- const result = this.execute(command);
232
+ SPAs use a single browser client with the anon key. All authorization is enforced via RLS. The service_role key is never present in the SPA bundle.
192
233
 
193
- // Emit domain event
194
- this.events.push(new SupabaseProcessedEvent(result));
234
+ ```typescript
235
+ // src/lib/supabase.ts
236
+ import { createClient } from '@supabase/supabase-js'
237
+ import type { Database } from './database.types'
238
+
239
+ // Singleton client — one instance for the entire SPA
240
+ export const supabase = createClient<Database>(
241
+ import.meta.env.VITE_SUPABASE_URL,
242
+ import.meta.env.VITE_SUPABASE_ANON_KEY,
243
+ {
244
+ auth: {
245
+ autoRefreshToken: true,
246
+ persistSession: true,
247
+ detectSessionInUrl: true, // handles OAuth redirects
248
+ storage: window.localStorage,
249
+ },
195
250
  }
251
+ )
196
252
 
197
- getUncommittedEvents(): DomainEvent[] {
198
- return [...this.events];
253
+ // Auth state listener — call once at app initialization
254
+ supabase.auth.onAuthStateChange((event, session) => {
255
+ if (event === 'SIGNED_OUT') {
256
+ // Clear local caches
257
+ queryClient.clear() // React Query
199
258
  }
200
- }
201
-
202
- // Event handler
203
- @EventHandler(SupabaseProcessedEvent)
204
- class SupabaseEventHandler {
205
- async handle(event: SupabaseProcessedEvent): Promise<void> {
206
- // Saga orchestration
207
- await this.sagaOrchestrator.continue(event);
259
+ if (event === 'TOKEN_REFRESHED') {
260
+ console.log('Token refreshed')
208
261
  }
209
- }
262
+ })
210
263
  ```
211
264
 
212
- ---
265
+ ### React Hook for Auth-Protected Queries
266
+
267
+ ```typescript
268
+ // src/hooks/useSupabaseQuery.ts
269
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
270
+ import { supabase } from '../lib/supabase'
271
+
272
+ export function useTodos() {
273
+ return useQuery({
274
+ queryKey: ['todos'],
275
+ queryFn: async () => {
276
+ const { data, error } = await supabase
277
+ .from('todos')
278
+ .select('id, title, is_complete, created_at')
279
+ .order('created_at', { ascending: false })
280
+
281
+ if (error) throw new Error(`Failed to load todos: ${error.message}`)
282
+ return data
283
+ },
284
+ })
285
+ }
213
286
 
214
- ## Decision Matrix
287
+ export function useCreateTodo() {
288
+ const queryClient = useQueryClient()
289
+
290
+ return useMutation({
291
+ mutationFn: async (title: string) => {
292
+ const { data, error } = await supabase
293
+ .from('todos')
294
+ .insert({ title })
295
+ .select('id, title, is_complete, created_at')
296
+ .single()
297
+
298
+ if (error) throw new Error(`Failed to create todo: ${error.message}`)
299
+ return data
300
+ },
301
+ onSuccess: () => {
302
+ queryClient.invalidateQueries({ queryKey: ['todos'] })
303
+ },
304
+ })
305
+ }
306
+ ```
215
307
 
216
- | Factor | Monolith | Service Layer | Microservice |
217
- |--------|----------|---------------|--------------|
218
- | Team Size | 1-5 | 5-20 | 20+ |
219
- | DAU | < 10K | 10K-100K | 100K+ |
220
- | Deployment Frequency | Weekly | Daily | Continuous |
221
- | Failure Isolation | None | Partial | Full |
222
- | Operational Complexity | Low | Medium | High |
223
- | Time to Market | Fastest | Moderate | Slowest |
308
+ ### Mobile Architecture (React Native with Expo)
224
309
 
225
- ## Migration Path
310
+ React Native needs `AsyncStorage` for session persistence and deep link handling for OAuth.
226
311
 
227
- ```
228
- Monolith Service Layer:
229
- 1. Extract Supabase code to service/
230
- 2. Add caching layer
231
- 3. Add background processing
232
-
233
- Service Layer Microservice:
234
- 1. Create dedicated supabase-service repo
235
- 2. Define gRPC contract
236
- 3. Add event bus
237
- 4. Deploy to Kubernetes
238
- 5. Migrate traffic gradually
312
+ ```typescript
313
+ // lib/supabase.ts (React Native)
314
+ import { createClient } from '@supabase/supabase-js'
315
+ import AsyncStorage from '@react-native-async-storage/async-storage'
316
+ import type { Database } from './database.types'
317
+
318
+ export const supabase = createClient<Database>(
319
+ process.env.EXPO_PUBLIC_SUPABASE_URL!,
320
+ process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!,
321
+ {
322
+ auth: {
323
+ storage: AsyncStorage,
324
+ autoRefreshToken: true,
325
+ persistSession: true,
326
+ detectSessionInUrl: false, // disabled for React Native
327
+ },
328
+ }
329
+ )
239
330
  ```
240
331
 
241
- ## Instructions
332
+ ### Mobile OAuth with Deep Links
242
333
 
243
- ### Step 1: Assess Requirements
244
- Use the decision matrix to identify appropriate variant.
334
+ ```typescript
335
+ // lib/auth.ts (React Native)
336
+ import { supabase } from './supabase'
337
+ import * as Linking from 'expo-linking'
338
+ import * as WebBrowser from 'expo-web-browser'
339
+
340
+ const redirectUrl = Linking.createURL('auth/callback')
341
+
342
+ export async function signInWithGoogle() {
343
+ const { data, error } = await supabase.auth.signInWithOAuth({
344
+ provider: 'google',
345
+ options: {
346
+ redirectTo: redirectUrl,
347
+ skipBrowserRedirect: true, // handle manually for RN
348
+ },
349
+ })
350
+
351
+ if (error) throw new Error(`OAuth failed: ${error.message}`)
352
+ if (!data.url) throw new Error('No OAuth URL returned')
353
+
354
+ // Open in-app browser
355
+ const result = await WebBrowser.openAuthSessionAsync(data.url, redirectUrl)
356
+
357
+ if (result.type === 'success') {
358
+ const url = new URL(result.url)
359
+ const params = new URLSearchParams(url.hash.substring(1))
360
+ const accessToken = params.get('access_token')
361
+ const refreshToken = params.get('refresh_token')
362
+
363
+ if (accessToken && refreshToken) {
364
+ const { error: sessionError } = await supabase.auth.setSession({
365
+ access_token: accessToken,
366
+ refresh_token: refreshToken,
367
+ })
368
+ if (sessionError) throw sessionError
369
+ }
370
+ }
371
+ }
372
+ ```
245
373
 
246
- ### Step 2: Choose Architecture
247
- Select Monolith, Service Layer, or Microservice based on needs.
374
+ ### App.json Deep Link Configuration (Expo)
375
+
376
+ ```json
377
+ {
378
+ "expo": {
379
+ "scheme": "myapp",
380
+ "plugins": [
381
+ [
382
+ "expo-linking",
383
+ {
384
+ "scheme": "myapp"
385
+ }
386
+ ]
387
+ ]
388
+ }
389
+ }
390
+ ```
248
391
 
249
- ### Step 3: Implement Structure
250
- Set up project layout following the chosen blueprint.
392
+ ## Step 3 Serverless (Edge Functions) and Multi-Tenant
251
393
 
252
- ### Step 4: Plan Migration Path
253
- Document upgrade path for future scaling.
394
+ See [serverless and multi-tenant patterns](references/serverless-and-multi-tenant.md) for Edge Function per-request clients, admin operation escalation, RLS-based multi-tenant isolation with schema, and tenant-scoped SDK queries.
254
395
 
255
396
  ## Output
256
- - Architecture variant selected
257
- - Project structure implemented
258
- - Migration path documented
259
- - Appropriate patterns applied
397
+
398
+ - Next.js SSR setup with server client (cookies-based auth), browser client, and middleware
399
+ - Server Actions using admin client with service_role for privileged operations
400
+ - SPA pattern with singleton client, React Query integration, and auth state listener
401
+ - React Native setup with AsyncStorage, deep link OAuth, and in-app browser
402
+ - Edge Function patterns for per-request auth and admin escalation
403
+ - Multi-tenant RLS isolation with tenant_members lookup and scoped queries
404
+ - Decision matrix for choosing the right architecture per stack
260
405
 
261
406
  ## Error Handling
407
+
262
408
  | Issue | Cause | Solution |
263
409
  |-------|-------|----------|
264
- | Over-engineering | Wrong variant choice | Start simpler |
265
- | Performance issues | Wrong layer | Add caching/async |
266
- | Team friction | Complex architecture | Simplify or train |
267
- | Deployment complexity | Microservice overhead | Consider service layer |
410
+ | `AuthSessionMissingError` in Server Component | Cookies not passed to Supabase client | Use `createServerClient` from `@supabase/ssr` with cookie handlers |
411
+ | OAuth redirect fails in React Native | Missing deep link scheme | Add `scheme` to app.json and configure Supabase redirect URL |
412
+ | service_role key in client bundle | Wrong env var prefix (`NEXT_PUBLIC_`) | Remove `NEXT_PUBLIC_` prefix; only server code should access it |
413
+ | Multi-tenant data leak | Missing RLS policy or missing tenant_id filter | Verify RLS is enabled and policies check `tenant_members` |
414
+ | Edge Function `auth.getUser()` returns null | Missing Authorization header | Forward user's JWT from the client call |
415
+ | Session not persisting on mobile | AsyncStorage not configured | Pass `AsyncStorage` in auth config; ensure package is installed |
268
416
 
269
417
  ## Examples
270
418
 
271
- ### Quick Variant Check
272
- ```bash
273
- # Count team size and DAU to select variant
274
- echo "Team: $(git log --format='%ae' | sort -u | wc -l) developers"
275
- echo "DAU: Check analytics dashboard"
419
+ ### Test Auth Flow End-to-End (Next.js)
420
+
421
+ ```typescript
422
+ // app/auth/callback/route.ts
423
+ import { createSupabaseServer } from '@/lib/supabase/server'
424
+ import { NextResponse } from 'next/server'
425
+
426
+ export async function GET(request: Request) {
427
+ const { searchParams } = new URL(request.url)
428
+ const code = searchParams.get('code')
429
+
430
+ if (code) {
431
+ const supabase = await createSupabaseServer()
432
+ const { error } = await supabase.auth.exchangeCodeForSession(code)
433
+ if (error) {
434
+ return NextResponse.redirect(new URL('/login?error=auth_failed', request.url))
435
+ }
436
+ }
437
+
438
+ return NextResponse.redirect(new URL('/dashboard', request.url))
439
+ }
440
+ ```
441
+
442
+ ### Verify Tenant Isolation
443
+
444
+ ```sql
445
+ -- Test that RLS properly isolates tenants
446
+ SET request.jwt.claims = '{"sub": "user-uuid-1"}';
447
+
448
+ -- Should only return projects for user-uuid-1's tenant
449
+ SELECT * FROM public.projects;
276
450
  ```
277
451
 
278
452
  ## Resources
279
- - [Monolith First](https://martinfowler.com/bliki/MonolithFirst.html)
280
- - [Microservices Guide](https://martinfowler.com/microservices/)
281
- - [Supabase Architecture Guide](https://supabase.com/docs/architecture)
453
+
454
+ - [Supabase SSR (Next.js)](https://supabase.com/docs/guides/auth/server-side/nextjs)
455
+ - [Supabase React Native](https://supabase.com/docs/guides/getting-started/tutorials/with-expo-react-native)
456
+ - [Supabase Edge Functions](https://supabase.com/docs/guides/functions)
457
+ - [Multi-Tenant with RLS](https://supabase.com/docs/guides/database/postgres/row-level-security)
458
+ - [Supabase Auth Deep Linking](https://supabase.com/docs/guides/auth/native-mobile-deep-linking)
459
+ - [@supabase/ssr Package](https://supabase.com/docs/guides/auth/server-side/overview)
282
460
 
283
461
  ## Next Steps
284
- For common anti-patterns, see `supabase-known-pitfalls`.
462
+
463
+ For common mistakes and anti-patterns to avoid, see `supabase-known-pitfalls`.