@intentsolutionsio/supabase-pack 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +17 -0
- package/000-docs/001-BL-LICN-license.txt +3 -0
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/package.json +43 -0
- package/skills/supabase-advanced-troubleshooting/SKILL.md +261 -0
- package/skills/supabase-architecture-variants/SKILL.md +284 -0
- package/skills/supabase-auth-storage-realtime-core/SKILL.md +73 -0
- package/skills/supabase-ci-integration/SKILL.md +124 -0
- package/skills/supabase-common-errors/SKILL.md +109 -0
- package/skills/supabase-cost-tuning/SKILL.md +201 -0
- package/skills/supabase-data-handling/SKILL.md +220 -0
- package/skills/supabase-debug-bundle/SKILL.md +111 -0
- package/skills/supabase-deploy-integration/SKILL.md +209 -0
- package/skills/supabase-enterprise-rbac/SKILL.md +222 -0
- package/skills/supabase-hello-world/SKILL.md +96 -0
- package/skills/supabase-incident-runbook/SKILL.md +203 -0
- package/skills/supabase-install-auth/SKILL.md +90 -0
- package/skills/supabase-known-pitfalls/SKILL.md +334 -0
- package/skills/supabase-load-scale/SKILL.md +274 -0
- package/skills/supabase-local-dev-loop/SKILL.md +117 -0
- package/skills/supabase-migration-deep-dive/SKILL.md +244 -0
- package/skills/supabase-multi-env-setup/SKILL.md +222 -0
- package/skills/supabase-observability/SKILL.md +250 -0
- package/skills/supabase-performance-tuning/SKILL.md +214 -0
- package/skills/supabase-policy-guardrails/SKILL.md +257 -0
- package/skills/supabase-prod-checklist/SKILL.md +119 -0
- package/skills/supabase-rate-limits/SKILL.md +149 -0
- package/skills/supabase-reference-architecture/SKILL.md +238 -0
- package/skills/supabase-reliability-patterns/SKILL.md +290 -0
- package/skills/supabase-schema-from-requirements/SKILL.md +71 -0
- package/skills/supabase-sdk-patterns/SKILL.md +147 -0
- package/skills/supabase-security-basics/SKILL.md +140 -0
- package/skills/supabase-upgrade-migration/SKILL.md +112 -0
- package/skills/supabase-webhooks-events/SKILL.md +199 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-known-pitfalls
|
|
3
|
+
description: |
|
|
4
|
+
Identify and avoid Supabase anti-patterns and common integration mistakes.
|
|
5
|
+
Use when reviewing Supabase code for issues, onboarding new developers,
|
|
6
|
+
or auditing existing Supabase integrations for best practices violations.
|
|
7
|
+
Trigger with phrases like "supabase mistakes", "supabase anti-patterns",
|
|
8
|
+
"supabase pitfalls", "supabase what not to do", "supabase code review".
|
|
9
|
+
allowed-tools: Read, Grep
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Known Pitfalls
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Common mistakes and anti-patterns when integrating with Supabase.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Access to Supabase codebase for review
|
|
22
|
+
- Understanding of async/await patterns
|
|
23
|
+
- Knowledge of security best practices
|
|
24
|
+
- Familiarity with rate limiting concepts
|
|
25
|
+
|
|
26
|
+
## Pitfall #1: Synchronous API Calls in Request Path
|
|
27
|
+
|
|
28
|
+
### ❌ Anti-Pattern
|
|
29
|
+
```typescript
|
|
30
|
+
// User waits for Supabase API call
|
|
31
|
+
app.post('/checkout', async (req, res) => {
|
|
32
|
+
const payment = await supabaseClient.processPayment(req.body); // 2-5s latency
|
|
33
|
+
const notification = await supabaseClient.sendEmail(payment); // Another 1-2s
|
|
34
|
+
res.json({ success: true }); // User waited 3-7s
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### ✅ Better Approach
|
|
39
|
+
```typescript
|
|
40
|
+
// Return immediately, process async
|
|
41
|
+
app.post('/checkout', async (req, res) => {
|
|
42
|
+
const jobId = await queue.enqueue('process-checkout', req.body);
|
|
43
|
+
res.json({ jobId, status: 'processing' }); // 50ms response
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Background job
|
|
47
|
+
async function processCheckout(data) {
|
|
48
|
+
const payment = await supabaseClient.processPayment(data);
|
|
49
|
+
await supabaseClient.sendEmail(payment);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Pitfall #2: Not Handling Rate Limits
|
|
56
|
+
|
|
57
|
+
### ❌ Anti-Pattern
|
|
58
|
+
```typescript
|
|
59
|
+
// Blast requests, crash on 429
|
|
60
|
+
for (const item of items) {
|
|
61
|
+
await supabaseClient.process(item); // Will hit rate limit
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### ✅ Better Approach
|
|
66
|
+
```typescript
|
|
67
|
+
import pLimit from 'p-limit';
|
|
68
|
+
|
|
69
|
+
const limit = pLimit(5); // Max 5 concurrent
|
|
70
|
+
const rateLimiter = new RateLimiter({ tokensPerSecond: 10 });
|
|
71
|
+
|
|
72
|
+
for (const item of items) {
|
|
73
|
+
await rateLimiter.acquire();
|
|
74
|
+
await limit(() => supabaseClient.process(item));
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Pitfall #3: Leaking API Keys
|
|
81
|
+
|
|
82
|
+
### ❌ Anti-Pattern
|
|
83
|
+
```typescript
|
|
84
|
+
// In frontend code (visible to users!)
|
|
85
|
+
const client = new SupabaseClient({
|
|
86
|
+
apiKey: 'sk_live_ACTUAL_KEY_HERE', // Anyone can see this
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// In git history
|
|
90
|
+
git commit -m "add API key" // Exposed forever
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### ✅ Better Approach
|
|
94
|
+
```typescript
|
|
95
|
+
// Backend only, environment variable
|
|
96
|
+
const client = new SupabaseClient({
|
|
97
|
+
apiKey: process.env.SUPABASE_API_KEY,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Use .gitignore
|
|
101
|
+
.env
|
|
102
|
+
.env.local
|
|
103
|
+
.env.*.local
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Pitfall #4: Ignoring Idempotency
|
|
109
|
+
|
|
110
|
+
### ❌ Anti-Pattern
|
|
111
|
+
```typescript
|
|
112
|
+
// Network error on response = duplicate charge!
|
|
113
|
+
try {
|
|
114
|
+
await supabaseClient.charge(order);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (error.code === 'NETWORK_ERROR') {
|
|
117
|
+
await supabaseClient.charge(order); // Charged twice!
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### ✅ Better Approach
|
|
123
|
+
```typescript
|
|
124
|
+
const idempotencyKey = `order-${order.id}-${Date.now()}`;
|
|
125
|
+
|
|
126
|
+
await supabaseClient.charge(order, {
|
|
127
|
+
idempotencyKey, // Safe to retry
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Pitfall #5: Not Validating Webhooks
|
|
134
|
+
|
|
135
|
+
### ❌ Anti-Pattern
|
|
136
|
+
```typescript
|
|
137
|
+
// Trust any incoming request
|
|
138
|
+
app.post('/webhook', (req, res) => {
|
|
139
|
+
processWebhook(req.body); // Attacker can send fake events
|
|
140
|
+
res.sendStatus(200);
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### ✅ Better Approach
|
|
145
|
+
```typescript
|
|
146
|
+
app.post('/webhook',
|
|
147
|
+
express.raw({ type: 'application/json' }),
|
|
148
|
+
(req, res) => {
|
|
149
|
+
const signature = req.headers['x-supabase-signature'];
|
|
150
|
+
if (!verifySupabaseSignature(req.body, signature)) {
|
|
151
|
+
return res.sendStatus(401);
|
|
152
|
+
}
|
|
153
|
+
processWebhook(JSON.parse(req.body));
|
|
154
|
+
res.sendStatus(200);
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Pitfall #6: Missing Error Handling
|
|
162
|
+
|
|
163
|
+
### ❌ Anti-Pattern
|
|
164
|
+
```typescript
|
|
165
|
+
// Crashes on any error
|
|
166
|
+
const result = await supabaseClient.get(id);
|
|
167
|
+
console.log(result.data.nested.value); // TypeError if missing
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### ✅ Better Approach
|
|
171
|
+
```typescript
|
|
172
|
+
try {
|
|
173
|
+
const result = await supabaseClient.get(id);
|
|
174
|
+
console.log(result?.data?.nested?.value ?? 'default');
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (error instanceof SupabaseNotFoundError) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
if (error instanceof SupabaseRateLimitError) {
|
|
180
|
+
await sleep(error.retryAfter);
|
|
181
|
+
return this.get(id); // Retry
|
|
182
|
+
}
|
|
183
|
+
throw error; // Rethrow unknown errors
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Pitfall #7: Hardcoding Configuration
|
|
190
|
+
|
|
191
|
+
### ❌ Anti-Pattern
|
|
192
|
+
```typescript
|
|
193
|
+
const client = new SupabaseClient({
|
|
194
|
+
timeout: 5000, // Too short for some operations
|
|
195
|
+
baseUrl: 'https://api.supabase.com', // Can't change for staging
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### ✅ Better Approach
|
|
200
|
+
```typescript
|
|
201
|
+
const client = new SupabaseClient({
|
|
202
|
+
timeout: parseInt(process.env.SUPABASE_TIMEOUT || '30000'),
|
|
203
|
+
baseUrl: process.env.SUPABASE_BASE_URL || 'https://api.supabase.com',
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Pitfall #8: Not Implementing Circuit Breaker
|
|
210
|
+
|
|
211
|
+
### ❌ Anti-Pattern
|
|
212
|
+
```typescript
|
|
213
|
+
// When Supabase is down, every request hangs
|
|
214
|
+
for (const user of users) {
|
|
215
|
+
await supabaseClient.sync(user); // All timeout sequentially
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### ✅ Better Approach
|
|
220
|
+
```typescript
|
|
221
|
+
import CircuitBreaker from 'opossum';
|
|
222
|
+
|
|
223
|
+
const breaker = new CircuitBreaker(supabaseClient.sync, {
|
|
224
|
+
timeout: 10000,
|
|
225
|
+
errorThresholdPercentage: 50,
|
|
226
|
+
resetTimeout: 30000,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Fails fast when circuit is open
|
|
230
|
+
for (const user of users) {
|
|
231
|
+
await breaker.fire(user).catch(handleFailure);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Pitfall #9: Logging Sensitive Data
|
|
238
|
+
|
|
239
|
+
### ❌ Anti-Pattern
|
|
240
|
+
```typescript
|
|
241
|
+
console.log('Request:', JSON.stringify(request)); // Logs API key, PII
|
|
242
|
+
console.log('User:', user); // Logs email, phone
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### ✅ Better Approach
|
|
246
|
+
```typescript
|
|
247
|
+
const redacted = {
|
|
248
|
+
...request,
|
|
249
|
+
apiKey: '[REDACTED]',
|
|
250
|
+
user: { id: user.id }, // Only non-sensitive fields
|
|
251
|
+
};
|
|
252
|
+
console.log('Request:', JSON.stringify(redacted));
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Pitfall #10: No Graceful Degradation
|
|
258
|
+
|
|
259
|
+
### ❌ Anti-Pattern
|
|
260
|
+
```typescript
|
|
261
|
+
// Entire feature broken if Supabase is down
|
|
262
|
+
const recommendations = await supabaseClient.getRecommendations(userId);
|
|
263
|
+
return renderPage({ recommendations }); // Page crashes
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### ✅ Better Approach
|
|
267
|
+
```typescript
|
|
268
|
+
let recommendations;
|
|
269
|
+
try {
|
|
270
|
+
recommendations = await supabaseClient.getRecommendations(userId);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
recommendations = await getFallbackRecommendations(userId);
|
|
273
|
+
reportDegradedService('supabase', error);
|
|
274
|
+
}
|
|
275
|
+
return renderPage({ recommendations, degraded: !recommendations });
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Instructions
|
|
281
|
+
|
|
282
|
+
### Step 1: Review for Anti-Patterns
|
|
283
|
+
Scan codebase for each pitfall pattern.
|
|
284
|
+
|
|
285
|
+
### Step 2: Prioritize Fixes
|
|
286
|
+
Address security issues first, then performance.
|
|
287
|
+
|
|
288
|
+
### Step 3: Implement Better Approach
|
|
289
|
+
Replace anti-patterns with recommended patterns.
|
|
290
|
+
|
|
291
|
+
### Step 4: Add Prevention
|
|
292
|
+
Set up linting and CI checks to prevent recurrence.
|
|
293
|
+
|
|
294
|
+
## Output
|
|
295
|
+
- Anti-patterns identified
|
|
296
|
+
- Fixes prioritized and implemented
|
|
297
|
+
- Prevention measures in place
|
|
298
|
+
- Code quality improved
|
|
299
|
+
|
|
300
|
+
## Error Handling
|
|
301
|
+
| Issue | Cause | Solution |
|
|
302
|
+
|-------|-------|----------|
|
|
303
|
+
| Too many findings | Legacy codebase | Prioritize security first |
|
|
304
|
+
| Pattern not detected | Complex code | Manual review |
|
|
305
|
+
| False positive | Similar code | Whitelist exceptions |
|
|
306
|
+
| Fix breaks tests | Behavior change | Update tests |
|
|
307
|
+
|
|
308
|
+
## Examples
|
|
309
|
+
|
|
310
|
+
### Quick Pitfall Scan
|
|
311
|
+
```bash
|
|
312
|
+
# Check for common pitfalls
|
|
313
|
+
grep -r "sk_live_" --include="*.ts" src/ # Key leakage
|
|
314
|
+
grep -r "console.log" --include="*.ts" src/ # Potential PII logging
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Resources
|
|
318
|
+
- [Supabase Security Guide](https://supabase.com/docs/security)
|
|
319
|
+
- [Supabase Best Practices](https://supabase.com/docs/best-practices)
|
|
320
|
+
|
|
321
|
+
## Quick Reference Card
|
|
322
|
+
|
|
323
|
+
| Pitfall | Detection | Prevention |
|
|
324
|
+
|---------|-----------|------------|
|
|
325
|
+
| Sync in request | High latency | Use queues |
|
|
326
|
+
| Rate limit ignore | 429 errors | Implement backoff |
|
|
327
|
+
| Key leakage | Git history scan | Env vars, .gitignore |
|
|
328
|
+
| No idempotency | Duplicate records | Idempotency keys |
|
|
329
|
+
| Unverified webhooks | Security audit | Signature verification |
|
|
330
|
+
| Missing error handling | Crashes | Try-catch, types |
|
|
331
|
+
| Hardcoded config | Code review | Environment variables |
|
|
332
|
+
| No circuit breaker | Cascading failures | opossum, resilience4j |
|
|
333
|
+
| Logging PII | Log audit | Redaction middleware |
|
|
334
|
+
| No degradation | Total outages | Fallback systems |
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-load-scale
|
|
3
|
+
description: |
|
|
4
|
+
Implement Supabase load testing, auto-scaling, and capacity planning strategies.
|
|
5
|
+
Use when running performance tests, configuring horizontal scaling,
|
|
6
|
+
or planning capacity for Supabase integrations.
|
|
7
|
+
Trigger with phrases like "supabase load test", "supabase scale",
|
|
8
|
+
"supabase performance test", "supabase capacity", "supabase k6", "supabase benchmark".
|
|
9
|
+
allowed-tools: Read, Write, Edit, Bash(k6:*), Bash(kubectl:*)
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Load & Scale
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Load testing, scaling strategies, and capacity planning for Supabase integrations.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- k6 load testing tool installed
|
|
22
|
+
- Kubernetes cluster with HPA configured
|
|
23
|
+
- Prometheus for metrics collection
|
|
24
|
+
- Test environment API keys
|
|
25
|
+
|
|
26
|
+
## Load Testing with k6
|
|
27
|
+
|
|
28
|
+
### Basic Load Test
|
|
29
|
+
```javascript
|
|
30
|
+
// supabase-load-test.js
|
|
31
|
+
import http from 'k6/http';
|
|
32
|
+
import { check, sleep } from 'k6';
|
|
33
|
+
|
|
34
|
+
export const options = {
|
|
35
|
+
stages: [
|
|
36
|
+
{ duration: '2m', target: 10 }, // Ramp up
|
|
37
|
+
{ duration: '5m', target: 10 }, // Steady state
|
|
38
|
+
{ duration: '2m', target: 50 }, // Ramp to peak
|
|
39
|
+
{ duration: '5m', target: 50 }, // Stress test
|
|
40
|
+
{ duration: '2m', target: 0 }, // Ramp down
|
|
41
|
+
],
|
|
42
|
+
thresholds: {
|
|
43
|
+
http_req_duration: ['p(95)<200'],
|
|
44
|
+
http_req_failed: ['rate<0.01'],
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default function () {
|
|
49
|
+
const response = http.post(
|
|
50
|
+
'https://api.supabase.com/v1/resource',
|
|
51
|
+
JSON.stringify({ test: true }),
|
|
52
|
+
{
|
|
53
|
+
headers: {
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'Authorization': `Bearer ${__ENV.SUPABASE_API_KEY}`,
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
check(response, {
|
|
61
|
+
'status is 200': (r) => r.status === 200,
|
|
62
|
+
'latency < 200ms': (r) => r.timings.duration < 200,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
sleep(1);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Run Load Test
|
|
70
|
+
```bash
|
|
71
|
+
# Install k6
|
|
72
|
+
brew install k6 # macOS
|
|
73
|
+
# or: sudo apt install k6 # Linux
|
|
74
|
+
|
|
75
|
+
# Run test
|
|
76
|
+
k6 run --env SUPABASE_API_KEY=${SUPABASE_API_KEY} supabase-load-test.js
|
|
77
|
+
|
|
78
|
+
# Run with output to InfluxDB
|
|
79
|
+
k6 run --out influxdb=http://localhost:8086/k6 supabase-load-test.js
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Scaling Patterns
|
|
83
|
+
|
|
84
|
+
### Horizontal Scaling
|
|
85
|
+
```yaml
|
|
86
|
+
# kubernetes HPA
|
|
87
|
+
apiVersion: autoscaling/v2
|
|
88
|
+
kind: HorizontalPodAutoscaler
|
|
89
|
+
metadata:
|
|
90
|
+
name: supabase-integration-hpa
|
|
91
|
+
spec:
|
|
92
|
+
scaleTargetRef:
|
|
93
|
+
apiVersion: apps/v1
|
|
94
|
+
kind: Deployment
|
|
95
|
+
name: supabase-integration
|
|
96
|
+
minReplicas: 2
|
|
97
|
+
maxReplicas: 20
|
|
98
|
+
metrics:
|
|
99
|
+
- type: Resource
|
|
100
|
+
resource:
|
|
101
|
+
name: cpu
|
|
102
|
+
target:
|
|
103
|
+
type: Utilization
|
|
104
|
+
averageUtilization: 70
|
|
105
|
+
- type: Pods
|
|
106
|
+
pods:
|
|
107
|
+
metric:
|
|
108
|
+
name: supabase_queue_depth
|
|
109
|
+
target:
|
|
110
|
+
type: AverageValue
|
|
111
|
+
averageValue: 100
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Connection Pooling
|
|
115
|
+
```typescript
|
|
116
|
+
import { Pool } from 'generic-pool';
|
|
117
|
+
|
|
118
|
+
const supabasePool = Pool.create({
|
|
119
|
+
create: async () => {
|
|
120
|
+
return new SupabaseClient({
|
|
121
|
+
apiKey: process.env.SUPABASE_API_KEY!,
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
destroy: async (client) => {
|
|
125
|
+
await client.close();
|
|
126
|
+
},
|
|
127
|
+
max: 20,
|
|
128
|
+
min: 5,
|
|
129
|
+
idleTimeoutMillis: 30000,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
async function withSupabaseClient<T>(
|
|
133
|
+
fn: (client: SupabaseClient) => Promise<T>
|
|
134
|
+
): Promise<T> {
|
|
135
|
+
const client = await supabasePool.acquire();
|
|
136
|
+
try {
|
|
137
|
+
return await fn(client);
|
|
138
|
+
} finally {
|
|
139
|
+
supabasePool.release(client);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Capacity Planning
|
|
145
|
+
|
|
146
|
+
### Metrics to Monitor
|
|
147
|
+
| Metric | Warning | Critical |
|
|
148
|
+
|--------|---------|----------|
|
|
149
|
+
| CPU Utilization | > 70% | > 85% |
|
|
150
|
+
| Memory Usage | > 75% | > 90% |
|
|
151
|
+
| Request Queue Depth | > 100 | > 500 |
|
|
152
|
+
| Error Rate | > 1% | > 5% |
|
|
153
|
+
| P95 Latency | > 500ms | > 2000ms |
|
|
154
|
+
|
|
155
|
+
### Capacity Calculation
|
|
156
|
+
```typescript
|
|
157
|
+
interface CapacityEstimate {
|
|
158
|
+
currentRPS: number;
|
|
159
|
+
maxRPS: number;
|
|
160
|
+
headroom: number;
|
|
161
|
+
scaleRecommendation: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function estimateSupabaseCapacity(
|
|
165
|
+
metrics: SystemMetrics
|
|
166
|
+
): CapacityEstimate {
|
|
167
|
+
const currentRPS = metrics.requestsPerSecond;
|
|
168
|
+
const avgLatency = metrics.p50Latency;
|
|
169
|
+
const cpuUtilization = metrics.cpuPercent;
|
|
170
|
+
|
|
171
|
+
// Estimate max RPS based on current performance
|
|
172
|
+
const maxRPS = currentRPS / (cpuUtilization / 100) * 0.7; // 70% target
|
|
173
|
+
const headroom = ((maxRPS - currentRPS) / currentRPS) * 100;
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
currentRPS,
|
|
177
|
+
maxRPS: Math.floor(maxRPS),
|
|
178
|
+
headroom: Math.round(headroom),
|
|
179
|
+
scaleRecommendation: headroom < 30
|
|
180
|
+
? 'Scale up soon'
|
|
181
|
+
: headroom < 50
|
|
182
|
+
? 'Monitor closely'
|
|
183
|
+
: 'Adequate capacity',
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Benchmark Results Template
|
|
189
|
+
|
|
190
|
+
```markdown
|
|
191
|
+
## Supabase Performance Benchmark
|
|
192
|
+
**Date:** YYYY-MM-DD
|
|
193
|
+
**Environment:** [staging/production]
|
|
194
|
+
**SDK Version:** X.Y.Z
|
|
195
|
+
|
|
196
|
+
### Test Configuration
|
|
197
|
+
- Duration: 10 minutes
|
|
198
|
+
- Ramp: 10 → 100 → 10 VUs
|
|
199
|
+
- Target endpoint: /v1/resource
|
|
200
|
+
|
|
201
|
+
### Results
|
|
202
|
+
| Metric | Value |
|
|
203
|
+
|--------|-------|
|
|
204
|
+
| Total Requests | 50,000 |
|
|
205
|
+
| Success Rate | 99.9% |
|
|
206
|
+
| P50 Latency | 120ms |
|
|
207
|
+
| P95 Latency | 350ms |
|
|
208
|
+
| P99 Latency | 800ms |
|
|
209
|
+
| Max RPS Achieved | 150 |
|
|
210
|
+
|
|
211
|
+
### Observations
|
|
212
|
+
- [Key finding 1]
|
|
213
|
+
- [Key finding 2]
|
|
214
|
+
|
|
215
|
+
### Recommendations
|
|
216
|
+
- [Scaling recommendation]
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Instructions
|
|
220
|
+
|
|
221
|
+
### Step 1: Create Load Test Script
|
|
222
|
+
Write k6 test script with appropriate thresholds.
|
|
223
|
+
|
|
224
|
+
### Step 2: Configure Auto-Scaling
|
|
225
|
+
Set up HPA with CPU and custom metrics.
|
|
226
|
+
|
|
227
|
+
### Step 3: Run Load Test
|
|
228
|
+
Execute test and collect metrics.
|
|
229
|
+
|
|
230
|
+
### Step 4: Analyze and Document
|
|
231
|
+
Record results in benchmark template.
|
|
232
|
+
|
|
233
|
+
## Output
|
|
234
|
+
- Load test script created
|
|
235
|
+
- HPA configured
|
|
236
|
+
- Benchmark results documented
|
|
237
|
+
- Capacity recommendations defined
|
|
238
|
+
|
|
239
|
+
## Error Handling
|
|
240
|
+
| Issue | Cause | Solution |
|
|
241
|
+
|-------|-------|----------|
|
|
242
|
+
| k6 timeout | Rate limited | Reduce RPS |
|
|
243
|
+
| HPA not scaling | Wrong metrics | Verify metric name |
|
|
244
|
+
| Connection refused | Pool exhausted | Increase pool size |
|
|
245
|
+
| Inconsistent results | Warm-up needed | Add ramp-up phase |
|
|
246
|
+
|
|
247
|
+
## Examples
|
|
248
|
+
|
|
249
|
+
### Quick k6 Test
|
|
250
|
+
```bash
|
|
251
|
+
k6 run --vus 10 --duration 30s supabase-load-test.js
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Check Current Capacity
|
|
255
|
+
```typescript
|
|
256
|
+
const metrics = await getSystemMetrics();
|
|
257
|
+
const capacity = estimateSupabaseCapacity(metrics);
|
|
258
|
+
console.log('Headroom:', capacity.headroom + '%');
|
|
259
|
+
console.log('Recommendation:', capacity.scaleRecommendation);
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Scale HPA Manually
|
|
263
|
+
```bash
|
|
264
|
+
kubectl scale deployment supabase-integration --replicas=5
|
|
265
|
+
kubectl get hpa supabase-integration-hpa
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Resources
|
|
269
|
+
- [k6 Documentation](https://k6.io/docs/)
|
|
270
|
+
- [Kubernetes HPA](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/)
|
|
271
|
+
- [Supabase Rate Limits](https://supabase.com/docs/rate-limits)
|
|
272
|
+
|
|
273
|
+
## Next Steps
|
|
274
|
+
For reliability patterns, see `supabase-reliability-patterns`.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-local-dev-loop
|
|
3
|
+
description: |
|
|
4
|
+
Configure Supabase local development with hot reload and testing.
|
|
5
|
+
Use when setting up a development environment, configuring test workflows,
|
|
6
|
+
or establishing a fast iteration cycle with Supabase.
|
|
7
|
+
Trigger with phrases like "supabase dev setup", "supabase local development",
|
|
8
|
+
"supabase dev environment", "develop with supabase".
|
|
9
|
+
allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(pnpm:*), Grep
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Local Dev Loop
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Set up a fast, reproducible local development workflow for Supabase.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Completed `supabase-install-auth` setup
|
|
22
|
+
- Node.js 18+ with npm/pnpm
|
|
23
|
+
- Code editor with TypeScript support
|
|
24
|
+
- Git for version control
|
|
25
|
+
|
|
26
|
+
## Instructions
|
|
27
|
+
|
|
28
|
+
### Step 1: Create Project Structure
|
|
29
|
+
```
|
|
30
|
+
my-supabase-project/
|
|
31
|
+
├── src/
|
|
32
|
+
│ ├── supabase/
|
|
33
|
+
│ │ ├── client.ts # Supabase client wrapper
|
|
34
|
+
│ │ ├── config.ts # Configuration management
|
|
35
|
+
│ │ └── utils.ts # Helper functions
|
|
36
|
+
│ └── index.ts
|
|
37
|
+
├── tests/
|
|
38
|
+
│ └── supabase.test.ts
|
|
39
|
+
├── .env.local # Local secrets (git-ignored)
|
|
40
|
+
├── .env.example # Template for team
|
|
41
|
+
└── package.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Step 2: Configure Environment
|
|
45
|
+
```bash
|
|
46
|
+
# Copy environment template
|
|
47
|
+
cp .env.example .env.local
|
|
48
|
+
|
|
49
|
+
# Install dependencies
|
|
50
|
+
npm install
|
|
51
|
+
|
|
52
|
+
# Start development server
|
|
53
|
+
npm run dev
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Step 3: Setup Hot Reload
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"scripts": {
|
|
60
|
+
"dev": "tsx watch src/index.ts",
|
|
61
|
+
"test": "vitest",
|
|
62
|
+
"test:watch": "vitest --watch"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Step 4: Configure Testing
|
|
68
|
+
```typescript
|
|
69
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
70
|
+
import { SupabaseClient } from '../src/supabase/client';
|
|
71
|
+
|
|
72
|
+
describe('Supabase Client', () => {
|
|
73
|
+
it('should initialize with API key', () => {
|
|
74
|
+
const client = new SupabaseClient({ apiKey: 'test-key' });
|
|
75
|
+
expect(client).toBeDefined();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Output
|
|
81
|
+
- Working development environment with hot reload
|
|
82
|
+
- Configured test suite with mocking
|
|
83
|
+
- Environment variable management
|
|
84
|
+
- Fast iteration cycle for Supabase development
|
|
85
|
+
|
|
86
|
+
## Error Handling
|
|
87
|
+
| Error | Cause | Solution |
|
|
88
|
+
|-------|-------|----------|
|
|
89
|
+
| Module not found | Missing dependency | Run `npm install` |
|
|
90
|
+
| Port in use | Another process | Kill process or change port |
|
|
91
|
+
| Env not loaded | Missing .env.local | Copy from .env.example |
|
|
92
|
+
| Test timeout | Slow network | Increase test timeout |
|
|
93
|
+
|
|
94
|
+
## Examples
|
|
95
|
+
|
|
96
|
+
### Mock Supabase Responses
|
|
97
|
+
```typescript
|
|
98
|
+
vi.mock('@supabase/supabase-js', () => ({
|
|
99
|
+
SupabaseClient: vi.fn().mockImplementation(() => ({
|
|
100
|
+
// Mock methods here
|
|
101
|
+
})),
|
|
102
|
+
}));
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Debug Mode
|
|
106
|
+
```bash
|
|
107
|
+
# Enable verbose logging
|
|
108
|
+
DEBUG=SUPABASE=* npm run dev
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Resources
|
|
112
|
+
- [Supabase SDK Reference](https://supabase.com/docs/sdk)
|
|
113
|
+
- [Vitest Documentation](https://vitest.dev/)
|
|
114
|
+
- [tsx Documentation](https://github.com/esbuild-kit/tsx)
|
|
115
|
+
|
|
116
|
+
## Next Steps
|
|
117
|
+
See `supabase-sdk-patterns` for production-ready code patterns.
|