@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,147 +1,436 @@
1
1
  ---
2
2
  name: supabase-sdk-patterns
3
- description: |
4
- Apply production-ready Supabase SDK patterns for TypeScript and Python.
5
- Use when implementing Supabase integrations, refactoring SDK usage,
6
- or establishing team coding standards for Supabase.
7
- Trigger with phrases like "supabase SDK patterns", "supabase best practices",
8
- "supabase code patterns", "idiomatic supabase".
9
- allowed-tools: Read, Write, Edit
3
+ description: 'Apply production-ready Supabase SDK patterns for TypeScript and Python
4
+ projects.
5
+
6
+ Use when implementing queries, auth, realtime, storage, or RPC calls
7
+
8
+ with @supabase/supabase-js or supabase-py.
9
+
10
+ Trigger with phrases like "supabase SDK patterns", "supabase query",
11
+
12
+ "supabase typescript", "supabase python", "supabase client setup",
13
+
14
+ "supabase realtime", "supabase auth", "supabase storage".
15
+
16
+ '
17
+ allowed-tools: Read, Write, Edit, Grep
10
18
  version: 1.0.0
11
19
  license: MIT
12
20
  author: Jeremy Longshore <jeremy@intentsolutions.io>
21
+ tags:
22
+ - saas
23
+ - supabase
24
+ - typescript
25
+ - python
26
+ - sdk
27
+ - patterns
28
+ compatibility: Designed for Claude Code, also compatible with Cursor
13
29
  ---
14
-
15
30
  # Supabase SDK Patterns
16
31
 
17
32
  ## Overview
18
- Production-ready patterns for Supabase SDK usage in TypeScript and Python.
33
+
34
+ Production patterns for `@supabase/supabase-js` v2 and `supabase-py`. Every Supabase query returns `{ data, error }` — never assume success. This skill covers client initialization, CRUD with filters, auth, realtime subscriptions, storage, RPC, and the Python equivalent for each pattern.
19
35
 
20
36
  ## Prerequisites
21
- - Completed `supabase-install-auth` setup
22
- - Familiarity with async/await patterns
23
- - Understanding of error handling best practices
37
+
38
+ - Supabase project with URL and anon key (or service role key for server-side)
39
+ - `@supabase/supabase-js` v2 installed (TypeScript) or `supabase` pip package (Python)
40
+ - TypeScript projects: generated database types via `supabase gen types typescript`
24
41
 
25
42
  ## Instructions
26
43
 
27
- ### Step 1: Implement Singleton Pattern (Recommended)
44
+ ### Step 1: Initialize a Typed Singleton Client
45
+
46
+ Create one client instance and reuse it. Never call `createClient` per-request.
47
+
28
48
  ```typescript
29
- // src/supabase/client.ts
30
- import { SupabaseClient } from '@supabase/supabase-js';
49
+ // lib/supabase.ts
50
+ import { createClient } from '@supabase/supabase-js'
51
+ import type { Database } from './database.types'
31
52
 
32
- let instance: SupabaseClient | null = null;
53
+ let supabase: ReturnType<typeof createClient<Database>>
33
54
 
34
- export function getSupabaseClient(): SupabaseClient {
35
- if (!instance) {
36
- instance = new SupabaseClient({
37
- apiKey: process.env.SUPABASE_API_KEY!,
38
- // Additional options
39
- });
55
+ export function getSupabase() {
56
+ if (!supabase) {
57
+ supabase = createClient<Database>(
58
+ process.env.SUPABASE_URL!,
59
+ process.env.SUPABASE_ANON_KEY!,
60
+ {
61
+ auth: { autoRefreshToken: true, persistSession: true },
62
+ db: { schema: 'public' },
63
+ global: { headers: { 'x-app-name': 'my-app' } },
64
+ }
65
+ )
40
66
  }
41
- return instance;
67
+ return supabase
42
68
  }
43
69
  ```
44
70
 
45
- ### Step 2: Add Error Handling Wrapper
71
+ **Python equivalent:**
72
+
73
+ ```python
74
+ from supabase import create_client, Client
75
+
76
+ _client: Client | None = None
77
+
78
+ def get_supabase() -> Client:
79
+ global _client
80
+ if _client is None:
81
+ _client = create_client(
82
+ os.environ["SUPABASE_URL"],
83
+ os.environ["SUPABASE_ANON_KEY"],
84
+ )
85
+ return _client
86
+ ```
87
+
88
+ ### Step 2: Query, Filter, and Mutate Data
89
+
90
+ **All queries return `{ data, error }`.** Always destructure and check error before using data.
91
+
92
+ **Select with filters and chaining:**
93
+
46
94
  ```typescript
47
- import { SupabaseError } from '@supabase/supabase-js';
48
-
49
- async function safeSupabaseCall<T>(
50
- operation: () => Promise<T>
51
- ): Promise<{ data: T | null; error: Error | null }> {
52
- try {
53
- const data = await operation();
54
- return { data, error: null };
55
- } catch (err) {
56
- if (err instanceof SupabaseError) {
57
- console.error({
58
- code: err.code,
59
- message: err.message,
60
- });
61
- }
62
- return { data: null, error: err as Error };
63
- }
95
+ const { data, error } = await getSupabase()
96
+ .from('users')
97
+ .select('id, name, email')
98
+ .eq('active', true) // WHERE active = true
99
+ .gt('age', 18) // AND age > 18
100
+ .ilike('name', '%john%') // AND name ILIKE '%john%'
101
+ .in('role', ['admin', 'editor']) // AND role IN (...)
102
+ .order('name', { ascending: true })
103
+ .limit(10)
104
+
105
+ if (error) throw error
106
+ // data is typed as Pick<User, 'id' | 'name' | 'email'>[]
107
+ ```
108
+
109
+ **Insert with select (return the inserted row):**
110
+
111
+ ```typescript
112
+ const { data: newUser, error } = await getSupabase()
113
+ .from('users')
114
+ .insert({ name: 'Alice', email: 'alice@example.com', active: true })
115
+ .select() // Without .select(), data is null
116
+ .single() // Unwrap from array to single object
117
+
118
+ if (error) throw error
119
+ // newUser is the full row with server-generated id, created_at, etc.
120
+ ```
121
+
122
+ **Upsert (insert or update on conflict):**
123
+
124
+ ```typescript
125
+ const { data, error } = await getSupabase()
126
+ .from('users')
127
+ .upsert(
128
+ { email: 'alice@example.com', name: 'Alice Updated' },
129
+ { onConflict: 'email' } // Match on unique column
130
+ )
131
+ .select()
132
+ .single()
133
+ ```
134
+
135
+ **Update and delete:**
136
+
137
+ ```typescript
138
+ // Update
139
+ const { data, error } = await getSupabase()
140
+ .from('users')
141
+ .update({ active: false })
142
+ .eq('id', userId)
143
+ .select()
144
+ .single()
145
+
146
+ // Delete
147
+ const { error } = await getSupabase()
148
+ .from('users')
149
+ .delete()
150
+ .eq('id', userId)
151
+ ```
152
+
153
+ **RPC — call a Postgres function:**
154
+
155
+ ```typescript
156
+ const { data, error } = await getSupabase()
157
+ .rpc('my_function', { arg1: 'value', arg2: 42 })
158
+
159
+ if (error) throw error
160
+ // data is the function's return value
161
+ ```
162
+
163
+ **Complete filter reference:**
164
+
165
+ | Filter | SQL Equivalent | Example |
166
+ |--------|---------------|---------|
167
+ | `.eq(col, val)` | `= val` | `.eq('status', 'active')` |
168
+ | `.neq(col, val)` | `!= val` | `.neq('role', 'guest')` |
169
+ | `.gt(col, val)` | `> val` | `.gt('age', 18)` |
170
+ | `.gte(col, val)` | `>= val` | `.gte('score', 90)` |
171
+ | `.lt(col, val)` | `< val` | `.lt('price', 100)` |
172
+ | `.lte(col, val)` | `<= val` | `.lte('quantity', 0)` |
173
+ | `.like(col, pat)` | `LIKE pat` | `.like('name', '%son')` |
174
+ | `.ilike(col, pat)` | `ILIKE pat` | `.ilike('email', '%@gmail%')` |
175
+ | `.is(col, val)` | `IS val` | `.is('deleted_at', null)` |
176
+ | `.in(col, arr)` | `IN (...)` | `.in('id', [1, 2, 3])` |
177
+ | `.contains(col, val)` | `@> val` | `.contains('tags', ['urgent'])` |
178
+ | `.range(from, to)` | `OFFSET/LIMIT` | `.range(0, 9)` (first 10 rows) |
179
+
180
+ **Python equivalent:**
181
+
182
+ ```python
183
+ # Select with filters
184
+ result = get_supabase() \
185
+ .table('users') \
186
+ .select('id, name, email') \
187
+ .eq('active', True) \
188
+ .gt('age', 18) \
189
+ .order('name') \
190
+ .limit(10) \
191
+ .execute()
192
+
193
+ if result.data is None:
194
+ raise Exception(f"Query failed")
195
+
196
+ # Insert
197
+ result = get_supabase().table('users').insert({
198
+ "name": "Alice", "email": "alice@example.com"
199
+ }).execute()
200
+
201
+ # Upsert
202
+ result = get_supabase().table('users').upsert({
203
+ "email": "alice@example.com", "name": "Alice Updated"
204
+ }).execute()
205
+
206
+ # RPC
207
+ result = get_supabase().rpc('my_function', {"arg1": "value"}).execute()
208
+ ```
209
+
210
+ ### Step 3: Auth, Realtime, and Storage
211
+
212
+ **Auth — sign up, sign in, get session:**
213
+
214
+ ```typescript
215
+ // Sign up
216
+ const { data, error } = await getSupabase().auth.signUp({
217
+ email: 'user@example.com',
218
+ password: 'securepassword',
219
+ })
220
+
221
+ // Sign in with password
222
+ const { data, error } = await getSupabase().auth.signInWithPassword({
223
+ email: 'user@example.com',
224
+ password: 'securepassword',
225
+ })
226
+ // data.session contains access_token, refresh_token
227
+ // data.user contains user metadata
228
+
229
+ // Get current session
230
+ const { data: { session } } = await getSupabase().auth.getSession()
231
+ if (!session) {
232
+ // User is not authenticated
64
233
  }
234
+
235
+ // Sign out
236
+ await getSupabase().auth.signOut()
237
+
238
+ // Listen for auth changes
239
+ getSupabase().auth.onAuthStateChange((event, session) => {
240
+ // event: 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | ...
241
+ console.log('Auth event:', event, session?.user?.email)
242
+ })
65
243
  ```
66
244
 
67
- ### Step 3: Implement Retry Logic
245
+ **Realtime subscribe to database changes:**
246
+
68
247
  ```typescript
69
- async function withRetry<T>(
70
- operation: () => Promise<T>,
71
- maxRetries = 3,
72
- backoffMs = 1000
73
- ): Promise<T> {
74
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
75
- try {
76
- return await operation();
77
- } catch (err) {
78
- if (attempt === maxRetries) throw err;
79
- const delay = backoffMs * Math.pow(2, attempt - 1);
80
- await new Promise(r => setTimeout(r, delay));
248
+ const channel = getSupabase()
249
+ .channel('room-messages')
250
+ .on(
251
+ 'postgres_changes',
252
+ {
253
+ event: '*', // 'INSERT' | 'UPDATE' | 'DELETE' | '*'
254
+ schema: 'public',
255
+ table: 'messages',
256
+ filter: 'room_id=eq.42', // Optional row-level filter
257
+ },
258
+ (payload) => {
259
+ console.log('Change:', payload.eventType, payload.new)
260
+ // payload.new = the new row (INSERT/UPDATE)
261
+ // payload.old = the old row (UPDATE/DELETE)
81
262
  }
82
- }
83
- throw new Error('Unreachable');
84
- }
263
+ )
264
+ .subscribe((status) => {
265
+ // status: 'SUBSCRIBED' | 'CLOSED' | 'CHANNEL_ERROR'
266
+ console.log('Subscription status:', status)
267
+ })
268
+
269
+ // Clean up when done
270
+ await getSupabase().removeChannel(channel)
271
+ ```
272
+
273
+ **Storage — upload, download, get public URL:**
274
+
275
+ ```typescript
276
+ // Upload a file
277
+ const { data, error } = await getSupabase().storage
278
+ .from('avatars') // bucket name
279
+ .upload('users/avatar.png', file, {
280
+ cacheControl: '3600',
281
+ upsert: true, // overwrite if exists
282
+ contentType: 'image/png',
283
+ })
284
+
285
+ // Download a file
286
+ const { data, error } = await getSupabase().storage
287
+ .from('avatars')
288
+ .download('users/avatar.png')
289
+ // data is a Blob
290
+
291
+ // Get public URL (no auth required if bucket is public)
292
+ const { data: { publicUrl } } = getSupabase().storage
293
+ .from('avatars')
294
+ .getPublicUrl('users/avatar.png')
295
+
296
+ // Get signed URL (time-limited access for private buckets)
297
+ const { data, error } = await getSupabase().storage
298
+ .from('documents')
299
+ .createSignedUrl('reports/q4.pdf', 3600) // expires in 1 hour
300
+ // data.signedUrl
85
301
  ```
86
302
 
87
303
  ## Output
88
- - Type-safe client singleton
89
- - Robust error handling with structured logging
90
- - Automatic retry with exponential backoff
91
- - Runtime validation for API responses
304
+
305
+ After applying these patterns you will have:
306
+
307
+ - Type-safe singleton client with `Database` generics
308
+ - CRUD operations using the full filter chain (eq, gt, in, ilike, etc.)
309
+ - Insert-with-select and upsert patterns that return the affected row
310
+ - Auth flows for sign-up, sign-in, session management, and state listeners
311
+ - Realtime subscriptions with row-level filtering and cleanup
312
+ - Storage upload/download with signed URLs for private buckets
313
+ - Python equivalents for all query patterns
92
314
 
93
315
  ## Error Handling
94
- | Pattern | Use Case | Benefit |
95
- |---------|----------|---------|
96
- | Safe wrapper | All API calls | Prevents uncaught exceptions |
97
- | Retry logic | Transient failures | Improves reliability |
98
- | Type guards | Response validation | Catches API changes |
99
- | Logging | All operations | Debugging and monitoring |
100
316
 
101
- ## Examples
317
+ Every Supabase call returns `{ data, error }`. Never skip the error check.
102
318
 
103
- ### Factory Pattern (Multi-tenant)
104
319
  ```typescript
105
- const clients = new Map<string, SupabaseClient>();
320
+ const { data, error } = await getSupabase().from('users').select('*')
106
321
 
107
- export function getClientForTenant(tenantId: string): SupabaseClient {
108
- if (!clients.has(tenantId)) {
109
- const apiKey = getTenantApiKey(tenantId);
110
- clients.set(tenantId, new SupabaseClient({ apiKey }));
111
- }
112
- return clients.get(tenantId)!;
322
+ if (error) {
323
+ // error is a PostgrestError with these fields:
324
+ // error.message — human-readable description
325
+ // error.code — Postgres error code (e.g., '23505')
326
+ // error.details — additional context
327
+ // error.hint — suggested fix from Postgres
328
+ console.error(`Query failed [${error.code}]: ${error.message}`)
329
+ throw error
113
330
  }
331
+
332
+ // Only safe to use data after error check
114
333
  ```
115
334
 
116
- ### Python Context Manager
117
- ```python
118
- from contextlib import asynccontextmanager
119
- from supabase import SupabaseClient
120
-
121
- @asynccontextmanager
122
- async def get_supabase_client():
123
- client = SupabaseClient()
124
- try:
125
- yield client
126
- finally:
127
- await client.close()
335
+ | Error Code | Meaning | What to Do |
336
+ |------------|---------|------------|
337
+ | `PGRST116` | No rows found (`.single()`) | Return null or 404, don't throw |
338
+ | `23505` | Unique constraint violation | Use `.upsert()` or show conflict error |
339
+ | `42501` | RLS policy violation | Check auth state and RLS policies |
340
+ | `PGRST000` | Connection error | Retry with exponential backoff |
341
+ | `42P01` | Table does not exist | Verify table name and run migrations |
342
+ | `23503` | Foreign key violation | Ensure referenced row exists first |
343
+ | `42703` | Column does not exist | Check column name, regenerate types |
344
+
345
+ ## Examples
346
+
347
+ **Service layer pattern (recommended for production):**
348
+
349
+ ```typescript
350
+ // services/user-service.ts
351
+ import type { Database } from '../lib/database.types'
352
+
353
+ type User = Database['public']['Tables']['users']['Row']
354
+ type UserInsert = Database['public']['Tables']['users']['Insert']
355
+
356
+ export const UserService = {
357
+ async getById(id: string): Promise<User | null> {
358
+ const { data, error } = await getSupabase()
359
+ .from('users')
360
+ .select('*')
361
+ .eq('id', id)
362
+ .single()
363
+
364
+ if (error?.code === 'PGRST116') return null // Not found
365
+ if (error) throw error
366
+ return data
367
+ },
368
+
369
+ async search(query: string, limit = 20): Promise<User[]> {
370
+ const { data, error } = await getSupabase()
371
+ .from('users')
372
+ .select('id, name, email, avatar_url')
373
+ .or(`name.ilike.%${query}%,email.ilike.%${query}%`)
374
+ .order('name')
375
+ .limit(limit)
376
+
377
+ if (error) throw error
378
+ return data
379
+ },
380
+
381
+ async createOrUpdate(user: UserInsert): Promise<User> {
382
+ const { data, error } = await getSupabase()
383
+ .from('users')
384
+ .upsert(user, { onConflict: 'email' })
385
+ .select()
386
+ .single()
387
+
388
+ if (error) throw error
389
+ return data
390
+ },
391
+ }
128
392
  ```
129
393
 
130
- ### Zod Validation
394
+ **Pagination helper:**
395
+
131
396
  ```typescript
132
- import { z } from 'zod';
397
+ async function paginate<T>(
398
+ table: string,
399
+ select: string,
400
+ { page = 1, pageSize = 20, orderBy = 'id' } = {}
401
+ ) {
402
+ const from = (page - 1) * pageSize
403
+ const to = from + pageSize - 1
133
404
 
134
- const supabaseResponseSchema = z.object({
135
- id: z.string(),
136
- status: z.enum(['active', 'inactive']),
137
- createdAt: z.string().datetime(),
138
- });
405
+ const { data, error, count } = await getSupabase()
406
+ .from(table)
407
+ .select(select, { count: 'exact' })
408
+ .order(orderBy)
409
+ .range(from, to)
410
+
411
+ if (error) throw error
412
+ return {
413
+ data: data as T[],
414
+ page,
415
+ pageSize,
416
+ total: count ?? 0,
417
+ totalPages: Math.ceil((count ?? 0) / pageSize),
418
+ }
419
+ }
420
+
421
+ // Usage
422
+ const result = await paginate<User>('users', 'id, name, email', { page: 2 })
139
423
  ```
140
424
 
141
425
  ## Resources
142
- - [Supabase SDK Reference](https://supabase.com/docs/sdk)
143
- - [Supabase API Types](https://supabase.com/docs/types)
144
- - [Zod Documentation](https://zod.dev/)
426
+
427
+ - [Supabase JS Client Reference](https://supabase.com/docs/reference/javascript/initializing)
428
+ - [TypeScript Support & Type Generation](https://supabase.com/docs/reference/javascript/typescript-support)
429
+ - [Supabase Auth Reference](https://supabase.com/docs/reference/javascript/auth-signup)
430
+ - [Realtime Guide](https://supabase.com/docs/guides/realtime)
431
+ - [Storage Guide](https://supabase.com/docs/guides/storage)
432
+ - [Python Client Reference](https://supabase.com/docs/reference/python/initializing)
145
433
 
146
434
  ## Next Steps
147
- Apply patterns in `supabase-core-workflow-a` for real-world usage.
435
+
436
+ For database schema design, see `supabase-schema-from-requirements`. For auth deep-dive with RLS policies, see `supabase-install-auth`. For realtime architecture patterns, see `supabase-auth-storage-realtime-core`.
@@ -0,0 +1,11 @@
1
+ # Error Handling Reference
2
+
3
+ | Pattern | Use Case | Benefit |
4
+ |---------|----------|---------|
5
+ | Safe wrapper | All API calls | Prevents uncaught exceptions |
6
+ | Retry logic | Transient failures | Improves reliability |
7
+ | Type guards | Response validation | Catches API changes |
8
+ | Logging | All operations | Debugging and monitoring |
9
+
10
+ ---
11
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
@@ -0,0 +1,45 @@
1
+ ## Examples
2
+
3
+ ### Factory Pattern (Multi-tenant)
4
+
5
+ ```typescript
6
+ const clients = new Map<string, SupabaseClient>();
7
+
8
+ export function getClientForTenant(tenantId: string): SupabaseClient {
9
+ if (!clients.has(tenantId)) {
10
+ const apiKey = getTenantApiKey(tenantId);
11
+ clients.set(tenantId, new SupabaseClient({ apiKey }));
12
+ }
13
+ return clients.get(tenantId)!;
14
+ }
15
+ ```
16
+
17
+ ### Python Context Manager
18
+
19
+ ```python
20
+ from contextlib import asynccontextmanager
21
+ from supabase import SupabaseClient
22
+
23
+ @asynccontextmanager
24
+ async def get_supabase_client():
25
+ client = SupabaseClient()
26
+ try:
27
+ yield client
28
+ finally:
29
+ await client.close()
30
+ ```
31
+
32
+ ### Zod Validation
33
+
34
+ ```typescript
35
+ import { z } from 'zod';
36
+
37
+ const supabaseResponseSchema = z.object({
38
+ id: z.string(),
39
+ status: z.enum(['active', 'inactive']),
40
+ createdAt: z.string().datetime(),
41
+ });
42
+ ```
43
+
44
+ ---
45
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
@@ -0,0 +1,67 @@
1
+ ## Implementation Guide
2
+
3
+ ### Step 1: Implement Singleton Pattern (Recommended)
4
+
5
+ ```typescript
6
+ // src/supabase/client.ts
7
+ import { SupabaseClient } from '@supabase/supabase-js';
8
+
9
+ let instance: SupabaseClient | null = null;
10
+
11
+ export function getSupabaseClient(): SupabaseClient {
12
+ if (!instance) {
13
+ instance = new SupabaseClient({
14
+ apiKey: process.env.SUPABASE_API_KEY!,
15
+ // Additional options
16
+ });
17
+ }
18
+ return instance;
19
+ }
20
+ ```
21
+
22
+ ### Step 2: Add Error Handling Wrapper
23
+
24
+ ```typescript
25
+ import { SupabaseError } from '@supabase/supabase-js';
26
+
27
+ async function safeSupabaseCall<T>(
28
+ operation: () => Promise<T>
29
+ ): Promise<{ data: T | null; error: Error | null }> {
30
+ try {
31
+ const data = await operation();
32
+ return { data, error: null };
33
+ } catch (err) {
34
+ if (err instanceof SupabaseError) {
35
+ console.error({
36
+ code: err.code,
37
+ message: err.message,
38
+ });
39
+ }
40
+ return { data: null, error: err as Error };
41
+ }
42
+ }
43
+ ```
44
+
45
+ ### Step 3: Implement Retry Logic
46
+
47
+ ```typescript
48
+ async function withRetry<T>(
49
+ operation: () => Promise<T>,
50
+ maxRetries = 3,
51
+ backoffMs = 1000
52
+ ): Promise<T> {
53
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
54
+ try {
55
+ return await operation();
56
+ } catch (err) {
57
+ if (attempt === maxRetries) throw err;
58
+ const delay = backoffMs * Math.pow(2, attempt - 1);
59
+ await new Promise(r => setTimeout(r, delay));
60
+ }
61
+ }
62
+ throw new Error('Unreachable');
63
+ }
64
+ ```
65
+
66
+ ---
67
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*