@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,149 +1,362 @@
1
1
  ---
2
2
  name: supabase-rate-limits
3
- description: |
4
- Implement Supabase rate limiting, backoff, and idempotency patterns.
5
- Use when handling rate limit errors, implementing retry logic,
6
- or optimizing API request throughput for Supabase.
7
- Trigger with phrases like "supabase rate limit", "supabase throttling",
8
- "supabase 429", "supabase retry", "supabase backoff".
9
- allowed-tools: Read, Write, Edit
3
+ description: 'Manage Supabase rate limits and quotas across all plan tiers.
4
+
5
+ Use when hitting 429 errors, configuring connection pooling,
6
+
7
+ optimizing API throughput, or understanding tier-specific quotas
8
+
9
+ for Auth, Storage, Realtime, and Edge Functions.
10
+
11
+ Trigger: "supabase rate limit", "supabase 429", "supabase throttle",
12
+
13
+ "supabase quota", "supabase connection pool", "supabase too many requests".
14
+
15
+ '
16
+ allowed-tools: Read, Write, Edit, Bash, Grep
10
17
  version: 1.0.0
11
18
  license: MIT
12
19
  author: Jeremy Longshore <jeremy@intentsolutions.io>
20
+ tags:
21
+ - saas
22
+ - supabase
23
+ - rate-limiting
24
+ - reliability
25
+ - quotas
26
+ compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw
13
27
  ---
14
-
15
28
  # Supabase Rate Limits
16
29
 
17
30
  ## Overview
18
- Handle Supabase rate limits gracefully with exponential backoff and idempotency.
31
+
32
+ Supabase enforces rate limits and quotas across every API surface — PostgREST, Auth, Storage, Realtime, and Edge Functions. Limits scale by plan tier. This skill covers the exact numbers per tier, connection pooling via Supavisor, retry/backoff patterns, pagination to reduce payload, and dashboard monitoring so you can stay within quotas and handle 429 errors gracefully.
19
33
 
20
34
  ## Prerequisites
21
- - Supabase SDK installed
22
- - Understanding of async/await patterns
23
- - Access to rate limit headers
35
+
36
+ - Active Supabase project (any tier)
37
+ - `@supabase/supabase-js` v2+ installed
38
+ - Project URL and anon/service-role key available
39
+ - Node.js 18+ or equivalent runtime
24
40
 
25
41
  ## Instructions
26
42
 
27
- ### Step 1: Understand Rate Limit Tiers
43
+ ### Step 1 Understand Rate Limits by Tier and Surface
44
+
45
+ Every Supabase project has per-surface limits that differ by plan. Know these numbers before you architect:
46
+
47
+ **API Request Limits**
48
+
49
+ | Metric | Free | Pro | Enterprise |
50
+ |--------|------|-----|------------|
51
+ | Requests per minute (RPM) | 500 | 5,000 | Unlimited (custom) |
52
+ | Requests per day (RPD) | 50,000 | 1,000,000 | Unlimited (custom) |
53
+
54
+ **Auth Rate Limits**
55
+
56
+ | Endpoint | Free | Pro |
57
+ |----------|------|-----|
58
+ | Signup | 30/hour per IP | Higher (configurable) |
59
+ | Sign-in (password) | 30/hour per IP | Higher (configurable) |
60
+ | Magic link / OTP | 4/hour per user | Configurable |
61
+ | Token refresh | 360/hour | 360/hour |
62
+
63
+ Auth limits are per-IP and per-user. Configure custom limits in Dashboard > Authentication > Rate Limits.
64
+
65
+ **Storage Bandwidth**
66
+
67
+ | Metric | Free | Pro |
68
+ |--------|------|-----|
69
+ | Storage size | 1 GB | 100 GB |
70
+ | Bandwidth | 2 GB/month | 250 GB/month |
71
+ | Max file size | 50 MB | 5 GB |
72
+ | Upload rate | Shared with API RPM | Shared with API RPM |
73
+
74
+ **Realtime Connections**
75
+
76
+ | Metric | Free | Pro |
77
+ |--------|------|-----|
78
+ | Concurrent connections | 200 | 500 |
79
+ | Messages per second | 100 | 500 |
80
+ | Channel joins | Shared with connection limit | Shared |
81
+
82
+ **Edge Functions**
83
+
84
+ | Metric | Free | Pro |
85
+ |--------|------|-----|
86
+ | Invocations/month | 500,000 | 2,000,000 |
87
+ | Execution time | 150s wall / 50ms CPU | 150s wall / 2s CPU |
88
+ | Memory | 256 MB | 256 MB |
89
+
90
+ **Database Connections**
28
91
 
29
- | Tier | Requests/min | Requests/day | Burst |
30
- |------|-------------|--------------|-------|
31
- | Free | 500 | 50,000 | 10 |
32
- | Pro | 5,000 | 1,000,000 | 50 |
33
- | Enterprise | Unlimited | Unlimited | 200 |
92
+ | Mode | Free | Pro |
93
+ |------|------|-----|
94
+ | Direct connections | 60 | 100+ |
95
+ | Pooled connections (Supavisor) | 200 | 1,500+ |
34
96
 
35
- ### Step 2: Implement Exponential Backoff with Jitter
97
+ ### Step 2 Configure Connection Pooling with Supavisor
98
+
99
+ Supavisor is Supabase's built-in connection pooler (replaced PgBouncer). It supports two modes:
100
+
101
+ **Transaction mode (port 6543)** — recommended for serverless:
36
102
 
37
103
  ```typescript
38
- async function withExponentialBackoff<T>(
39
- operation: () => Promise<T>,
40
- config = { maxRetries: 5, baseDelayMs: 1000, maxDelayMs: 32000, jitterMs: 500 }
104
+ import { createClient } from '@supabase/supabase-js'
105
+
106
+ // Transaction mode: connections returned to pool after each transaction
107
+ // Best for: serverless functions, Edge Functions, high-concurrency apps
108
+ const supabase = createClient(
109
+ 'https://your-project.supabase.co',
110
+ process.env.SUPABASE_ANON_KEY!,
111
+ {
112
+ db: {
113
+ // Use the pooler connection string with port 6543
114
+ // Format: postgresql://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
115
+ }
116
+ }
117
+ )
118
+
119
+ // For direct Postgres connections (e.g., Prisma, Drizzle), add pgbouncer=true
120
+ // Connection string: postgresql://...@pooler.supabase.com:6543/postgres?pgbouncer=true
121
+ ```
122
+
123
+ **Session mode (port 5432)** — for LISTEN/NOTIFY and prepared statements:
124
+
125
+ ```typescript
126
+ // Session mode: dedicated connection per client session
127
+ // Best for: long-lived connections, LISTEN/NOTIFY, prepared statements
128
+ // Connection string: postgresql://...@pooler.supabase.com:5432/postgres
129
+ ```
130
+
131
+ **When to use which mode:**
132
+
133
+ | Use case | Mode | Port |
134
+ |----------|------|------|
135
+ | Serverless / Edge Functions | Transaction | 6543 |
136
+ | Next.js API routes | Transaction | 6543 |
137
+ | Long-running workers | Session | 5432 |
138
+ | Realtime subscriptions | Direct (no pooler) | 5432 |
139
+ | Prisma / Drizzle ORM | Transaction + `?pgbouncer=true` | 6543 |
140
+
141
+ ### Step 3 — Implement Retry, Pagination, and Monitoring
142
+
143
+ **Retry with exponential backoff for 429 errors:**
144
+
145
+ ```typescript
146
+ import { createClient, SupabaseClient } from '@supabase/supabase-js'
147
+
148
+ const supabase = createClient(
149
+ process.env.SUPABASE_URL!,
150
+ process.env.SUPABASE_ANON_KEY!
151
+ )
152
+
153
+ interface RetryConfig {
154
+ maxRetries: number
155
+ baseDelayMs: number
156
+ maxDelayMs: number
157
+ }
158
+
159
+ async function withRetry<T>(
160
+ operation: () => Promise<{ data: T | null; error: any }>,
161
+ config: RetryConfig = { maxRetries: 3, baseDelayMs: 500, maxDelayMs: 10_000 }
41
162
  ): Promise<T> {
42
163
  for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
43
- try {
44
- return await operation();
45
- } catch (error: any) {
46
- if (attempt === config.maxRetries) throw error;
47
- const status = error.status || error.response?.status;
48
- if (status !== 429 && (status < 500 || status >= 600)) throw error;
49
-
50
- // Exponential delay with jitter to prevent thundering herd
51
- const exponentialDelay = config.baseDelayMs * Math.pow(2, attempt);
52
- const jitter = Math.random() * config.jitterMs;
53
- const delay = Math.min(exponentialDelay + jitter, config.maxDelayMs);
54
-
55
- console.log(`Rate limited. Retrying in ${delay.toFixed(0)}ms...`);
56
- await new Promise(r => setTimeout(r, delay));
164
+ const { data, error } = await operation()
165
+
166
+ if (!error) return data as T
167
+
168
+ const isRetryable =
169
+ error.message?.includes('rate limit') ||
170
+ error.message?.includes('too many requests') ||
171
+ error.code === '429' ||
172
+ error.code === 'PGRST000' // connection pool exhausted
173
+
174
+ if (!isRetryable || attempt === config.maxRetries) {
175
+ throw new Error(`Supabase error after ${attempt + 1} attempts: ${error.message}`)
57
176
  }
177
+
178
+ // Check Retry-After header if available
179
+ const retryAfter = error.details?.retryAfter
180
+ const delay = retryAfter
181
+ ? retryAfter * 1000
182
+ : Math.min(
183
+ config.baseDelayMs * Math.pow(2, attempt) + Math.random() * 200,
184
+ config.maxDelayMs
185
+ )
186
+
187
+ console.warn(`[supabase-retry] Attempt ${attempt + 1}/${config.maxRetries}, waiting ${delay}ms`)
188
+ await new Promise((resolve) => setTimeout(resolve, delay))
58
189
  }
59
- throw new Error('Unreachable');
190
+
191
+ throw new Error('Unreachable')
60
192
  }
193
+
194
+ // Usage — wraps any Supabase query
195
+ const users = await withRetry(() =>
196
+ supabase.from('users').select('id, email, created_at').eq('active', true)
197
+ )
61
198
  ```
62
199
 
63
- ### Step 3: Add Idempotency Keys
200
+ **Pagination to reduce payload and stay within limits:**
64
201
 
65
202
  ```typescript
66
- import { v4 as uuidv4 } from 'uuid';
67
- import crypto from 'crypto';
203
+ // Use .range() to paginate reduces response size and avoids timeouts
204
+ async function fetchPaginated<T>(
205
+ table: string,
206
+ pageSize = 100,
207
+ filters?: (query: any) => any
208
+ ): Promise<T[]> {
209
+ const allRows: T[] = []
210
+ let from = 0
211
+
212
+ while (true) {
213
+ let query = supabase.from(table).select('*', { count: 'exact' })
214
+ if (filters) query = filters(query)
215
+
216
+ const { data, error, count } = await query.range(from, from + pageSize - 1)
217
+
218
+ if (error) throw error
219
+ if (!data || data.length === 0) break
220
+
221
+ allRows.push(...(data as T[]))
222
+ from += pageSize
68
223
 
69
- // Generate deterministic key from operation params (for safe retries)
70
- function generateIdempotencyKey(operation: string, params: Record<string, any>): string {
71
- const data = JSON.stringify({ operation, params });
72
- return crypto.createHash('sha256').update(data).digest('hex');
224
+ // Stop if we've fetched everything
225
+ if (count !== null && from >= count) break
226
+ }
227
+
228
+ return allRows
73
229
  }
74
230
 
75
- async function idempotentRequest<T>(
76
- client: SupabaseClient,
77
- params: Record<string, any>,
78
- idempotencyKey?: string // Pass existing key for retries
79
- ): Promise<T> {
80
- // Use provided key (for retries) or generate deterministic key from params
81
- const key = idempotencyKey || generateIdempotencyKey(params.method || 'POST', params);
82
- return client.request({
83
- ...params,
84
- headers: { 'Idempotency-Key': key, ...params.headers },
85
- });
231
+ // Usage
232
+ const allProducts = await fetchPaginated('products', 100, (q) =>
233
+ q.eq('status', 'active').order('created_at', { ascending: false })
234
+ )
235
+
236
+ // Simple single-page fetch with .range()
237
+ const { data } = await supabase
238
+ .from('orders')
239
+ .select('id, total, status')
240
+ .range(0, 99) // First 100 rows (0-indexed)
241
+ .order('created_at', { ascending: false })
242
+ ```
243
+
244
+ **Monitor usage via the Dashboard:**
245
+
246
+ 1. Navigate to Dashboard > Reports > API Usage
247
+ 2. Check the "API Requests" chart for RPM/RPD trends
248
+ 3. Review "Database" section for connection count and pool utilization
249
+ 4. Set up alerts in Dashboard > Settings > Notifications for:
250
+ - API request threshold (e.g., 80% of RPM limit)
251
+ - Database connection saturation
252
+ - Storage bandwidth approaching limit
253
+
254
+ **Batch operations to reduce request count:**
255
+
256
+ ```typescript
257
+ // BAD: N individual inserts = N requests against your RPM
258
+ // for (const item of items) await supabase.from('items').insert(item)
259
+
260
+ // GOOD: single batch insert (max ~1000 rows per request)
261
+ const { data, error } = await supabase
262
+ .from('items')
263
+ .upsert(batchOfItems, { onConflict: 'external_id' })
264
+ .select()
265
+
266
+ // For larger batches, chunk into groups
267
+ function chunk<T>(arr: T[], size: number): T[][] {
268
+ return Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>
269
+ arr.slice(i * size, i * size + size)
270
+ )
271
+ }
272
+
273
+ for (const batch of chunk(largeDataset, 500)) {
274
+ await withRetry(() =>
275
+ supabase.from('items').upsert(batch, { onConflict: 'external_id' }).select()
276
+ )
86
277
  }
87
278
  ```
88
279
 
89
280
  ## Output
90
- - Reliable API calls with automatic retry
91
- - Idempotent requests preventing duplicates
92
- - Rate limit headers properly handled
281
+
282
+ After applying this skill you will have:
283
+
284
+ - Clear understanding of rate limits per tier (Free: 500 RPM / 50K RPD, Pro: 5K RPM / 1M RPD)
285
+ - Connection pooling configured via Supavisor (port 6543 transaction mode for serverless)
286
+ - Retry wrapper with exponential backoff handling 429 errors
287
+ - Paginated queries using `.range(0, 99)` to reduce payload size
288
+ - Batch upsert pattern reducing N requests to 1
289
+ - Dashboard monitoring configured for API usage alerts
93
290
 
94
291
  ## Error Handling
95
- | Header | Description | Action |
96
- |--------|-------------|--------|
97
- | X-RateLimit-Limit | Max requests | Monitor usage |
98
- | X-RateLimit-Remaining | Remaining requests | Throttle if low |
99
- | X-RateLimit-Reset | Reset timestamp | Wait until reset |
100
- | Retry-After | Seconds to wait | Honor this value |
292
+
293
+ | Error | Cause | Solution |
294
+ |-------|-------|----------|
295
+ | `429 Too Many Requests` | Exceeded RPM or RPD limit | Apply `withRetry` backoff; reduce concurrency; upgrade tier |
296
+ | `PGRST000: could not connect` | Connection pool exhausted | Switch to Supavisor transaction mode (port 6543); reduce concurrent queries |
297
+ | Auth `over_request_rate_limit` | Too many signups/logins from one IP | Add CAPTCHA; configure custom auth rate limits in Dashboard |
298
+ | Storage `413 Payload Too Large` | File exceeds tier limit | Use TUS resumable upload; check tier file size limit |
299
+ | Realtime `too_many_connections` | Concurrent connection limit reached | Unsubscribe unused channels; upgrade to Pro for 500 connections |
300
+ | Edge Function `BOOT_ERROR` | Cold start timeout or memory exceeded | Reduce bundle size; avoid large imports at top level |
301
+ | `pgbouncer=true` errors with Prisma | Missing connection string parameter | Append `?pgbouncer=true` to pooler connection string on port 6543 |
101
302
 
102
303
  ## Examples
103
304
 
104
- ### Queue-Based Rate Limiting
305
+ **Example 1 — Serverless Edge Function with rate-limit-safe client:**
306
+
105
307
  ```typescript
106
- import PQueue from 'p-queue';
308
+ // supabase/functions/process-webhook/index.ts
309
+ import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'
310
+ import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
107
311
 
108
- const queue = new PQueue({
109
- concurrency: 5,
110
- interval: 1000,
111
- intervalCap: 10,
112
- });
312
+ serve(async (req) => {
313
+ const supabase = createClient(
314
+ Deno.env.get('SUPABASE_URL')!,
315
+ Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
316
+ )
113
317
 
114
- async function queuedRequest<T>(operation: () => Promise<T>): Promise<T> {
115
- return queue.add(operation);
116
- }
117
- ```
318
+ const payload = await req.json()
118
319
 
119
- ### Monitor Rate Limit Usage
120
- ```typescript
121
- class RateLimitMonitor {
122
- private remaining: number = 60;
123
- private resetAt: Date = new Date();
124
-
125
- updateFromHeaders(headers: Headers) {
126
- this.remaining = parseInt(headers.get('X-RateLimit-Remaining') || '60');
127
- const resetTimestamp = headers.get('X-RateLimit-Reset');
128
- if (resetTimestamp) {
129
- this.resetAt = new Date(parseInt(resetTimestamp) * 1000);
130
- }
131
- }
320
+ // Batch insert webhook events (single request vs N)
321
+ const { error } = await supabase
322
+ .from('webhook_events')
323
+ .insert(payload.events.map((e: any) => ({
324
+ type: e.type,
325
+ data: e.data,
326
+ received_at: new Date().toISOString(),
327
+ })))
132
328
 
133
- shouldThrottle(): boolean {
134
- // Only throttle if low remaining AND reset hasn't happened yet
135
- return this.remaining < 5 && new Date() < this.resetAt;
329
+ if (error) {
330
+ console.error('Insert failed:', error.message)
331
+ return new Response(JSON.stringify({ error: error.message }), { status: 500 })
136
332
  }
137
333
 
138
- getWaitTime(): number {
139
- return Math.max(0, this.resetAt.getTime() - Date.now());
140
- }
141
- }
334
+ return new Response(JSON.stringify({ processed: payload.events.length }), { status: 200 })
335
+ })
336
+ ```
337
+
338
+ **Example 2 — Connection string selection for different runtimes:**
339
+
340
+ ```bash
341
+ # Serverless (Vercel, Netlify, Edge Functions) — transaction mode
342
+ DATABASE_URL="postgresql://postgres.abc123:password@aws-0-us-east-1.pooler.supabase.com:6543/postgres?pgbouncer=true"
343
+
344
+ # Long-running server (Express, Fastify) — session mode
345
+ DATABASE_URL="postgresql://postgres.abc123:password@aws-0-us-east-1.pooler.supabase.com:5432/postgres"
346
+
347
+ # Direct connection (migrations, schema changes only)
348
+ DATABASE_URL="postgresql://postgres:password@db.abc123.supabase.co:5432/postgres"
142
349
  ```
143
350
 
144
351
  ## Resources
145
- - [Supabase Rate Limits](https://supabase.com/docs/rate-limits)
146
- - [p-queue Documentation](https://github.com/sindresorhus/p-queue)
352
+
353
+ - [Supabase Platform Limits & Quotas](https://supabase.com/docs/guides/platform/going-into-prod#rate-limiting)
354
+ - [Supavisor Connection Pooling](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler)
355
+ - Auth Rate Limits Configuration
356
+ - [Edge Functions Limits](https://supabase.com/docs/guides/functions/limits)
357
+ - [Storage Limits](https://supabase.com/docs/guides/storage#limits)
358
+ - [@supabase/supabase-js Reference](https://supabase.com/docs/reference/javascript/introduction)
147
359
 
148
360
  ## Next Steps
149
- For security configuration, see `supabase-security-basics`.
361
+
362
+ For securing your Supabase project with RLS policies and API key management, see `supabase-security-basics`. For optimizing database queries and indexing, see `supabase-performance-tuning`.
@@ -0,0 +1,11 @@
1
+ # Error Handling Reference
2
+
3
+ | Header | Description | Action |
4
+ |--------|-------------|--------|
5
+ | X-RateLimit-Limit | Max requests | Monitor usage |
6
+ | X-RateLimit-Remaining | Remaining requests | Throttle if low |
7
+ | X-RateLimit-Reset | Reset timestamp | Wait until reset |
8
+ | Retry-After | Seconds to wait | Honor this value |
9
+
10
+ ---
11
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
@@ -0,0 +1,46 @@
1
+ ## Examples
2
+
3
+ ### Queue-Based Rate Limiting
4
+
5
+ ```typescript
6
+ import PQueue from 'p-queue';
7
+
8
+ const queue = new PQueue({
9
+ concurrency: 5,
10
+ interval: 1000,
11
+ intervalCap: 10,
12
+ });
13
+
14
+ async function queuedRequest<T>(operation: () => Promise<T>): Promise<T> {
15
+ return queue.add(operation);
16
+ }
17
+ ```
18
+
19
+ ### Monitor Rate Limit Usage
20
+
21
+ ```typescript
22
+ class RateLimitMonitor {
23
+ private remaining: number = 60;
24
+ private resetAt: Date = new Date();
25
+
26
+ updateFromHeaders(headers: Headers) {
27
+ this.remaining = parseInt(headers.get('X-RateLimit-Remaining') || '60');
28
+ const resetTimestamp = headers.get('X-RateLimit-Reset');
29
+ if (resetTimestamp) {
30
+ this.resetAt = new Date(parseInt(resetTimestamp) * 1000);
31
+ }
32
+ }
33
+
34
+ shouldThrottle(): boolean {
35
+ // Only throttle if low remaining AND reset hasn't happened yet
36
+ return this.remaining < 5 && new Date() < this.resetAt;
37
+ }
38
+
39
+ getWaitTime(): number {
40
+ return Math.max(0, this.resetAt.getTime() - Date.now());
41
+ }
42
+ }
43
+ ```
44
+
45
+ ---
46
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
@@ -0,0 +1,66 @@
1
+ ## Implementation Guide
2
+
3
+ ### Step 1: Understand Rate Limit Tiers
4
+
5
+ | Tier | Requests/min | Requests/day | Burst |
6
+ |------|-------------|--------------|-------|
7
+ | Free | 500 | 50,000 | 10 |
8
+ | Pro | 5,000 | 1,000,000 | 50 |
9
+ | Enterprise | Unlimited | Unlimited | 200 |
10
+
11
+ ### Step 2: Implement Exponential Backoff with Jitter
12
+
13
+ ```typescript
14
+ async function withExponentialBackoff<T>(
15
+ operation: () => Promise<T>,
16
+ config = { maxRetries: 5, baseDelayMs: 1000, maxDelayMs: 32000, jitterMs: 500 }
17
+ ): Promise<T> {
18
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
19
+ try {
20
+ return await operation();
21
+ } catch (error: any) {
22
+ if (attempt === config.maxRetries) throw error;
23
+ const status = error.status || error.response?.status;
24
+ if (status !== 429 && (status < 500 || status >= 600)) throw error;
25
+
26
+ // Exponential delay with jitter to prevent thundering herd
27
+ const exponentialDelay = config.baseDelayMs * Math.pow(2, attempt);
28
+ const jitter = Math.random() * config.jitterMs;
29
+ const delay = Math.min(exponentialDelay + jitter, config.maxDelayMs);
30
+
31
+ console.log(`Rate limited. Retrying in ${delay.toFixed(0)}ms...`);
32
+ await new Promise(r => setTimeout(r, delay));
33
+ }
34
+ }
35
+ throw new Error('Unreachable');
36
+ }
37
+ ```
38
+
39
+ ### Step 3: Add Idempotency Keys
40
+
41
+ ```typescript
42
+ import { v4 as uuidv4 } from 'uuid';
43
+ import crypto from 'crypto';
44
+
45
+ // Generate deterministic key from operation params (for safe retries)
46
+ function generateIdempotencyKey(operation: string, params: Record<string, any>): string {
47
+ const data = JSON.stringify({ operation, params });
48
+ return crypto.createHash('sha256').update(data).digest('hex');
49
+ }
50
+
51
+ async function idempotentRequest<T>(
52
+ client: SupabaseClient,
53
+ params: Record<string, any>,
54
+ idempotencyKey?: string // Pass existing key for retries
55
+ ): Promise<T> {
56
+ // Use provided key (for retries) or generate deterministic key from params
57
+ const key = idempotencyKey || generateIdempotencyKey(params.method || 'POST', params);
58
+ return client.request({
59
+ ...params,
60
+ headers: { 'Idempotency-Key': key, ...params.headers },
61
+ });
62
+ }
63
+ ```
64
+
65
+ ---
66
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*