@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,214 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-performance-tuning
|
|
3
|
+
description: |
|
|
4
|
+
Optimize Supabase API performance with caching, batching, and connection pooling.
|
|
5
|
+
Use when experiencing slow API responses, implementing caching strategies,
|
|
6
|
+
or optimizing request throughput for Supabase integrations.
|
|
7
|
+
Trigger with phrases like "supabase performance", "optimize supabase",
|
|
8
|
+
"supabase latency", "supabase caching", "supabase slow", "supabase batch".
|
|
9
|
+
allowed-tools: Read, Write, Edit
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Performance Tuning
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Optimize Supabase API performance with caching, batching, and connection pooling.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Supabase SDK installed
|
|
22
|
+
- Understanding of async patterns
|
|
23
|
+
- Redis or in-memory cache available (optional)
|
|
24
|
+
- Performance monitoring in place
|
|
25
|
+
|
|
26
|
+
## Latency Benchmarks
|
|
27
|
+
|
|
28
|
+
| Operation | P50 | P95 | P99 |
|
|
29
|
+
|-----------|-----|-----|-----|
|
|
30
|
+
| Select | 15ms | 50ms | 100ms |
|
|
31
|
+
| Insert | 25ms | 75ms | 150ms |
|
|
32
|
+
| Real-time Subscribe | 50ms | 150ms | 300ms |
|
|
33
|
+
|
|
34
|
+
## Caching Strategy
|
|
35
|
+
|
|
36
|
+
### Response Caching
|
|
37
|
+
```typescript
|
|
38
|
+
import { LRUCache } from 'lru-cache';
|
|
39
|
+
|
|
40
|
+
const cache = new LRUCache<string, any>({
|
|
41
|
+
max: 1000,
|
|
42
|
+
ttl: 30000, // 1 minute
|
|
43
|
+
updateAgeOnGet: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
async function cachedSupabaseRequest<T>(
|
|
47
|
+
key: string,
|
|
48
|
+
fetcher: () => Promise<T>,
|
|
49
|
+
ttl?: number
|
|
50
|
+
): Promise<T> {
|
|
51
|
+
const cached = cache.get(key);
|
|
52
|
+
if (cached) return cached as T;
|
|
53
|
+
|
|
54
|
+
const result = await fetcher();
|
|
55
|
+
cache.set(key, result, { ttl });
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Redis Caching (Distributed)
|
|
61
|
+
```typescript
|
|
62
|
+
import Redis from 'ioredis';
|
|
63
|
+
|
|
64
|
+
const redis = new Redis(process.env.REDIS_URL);
|
|
65
|
+
|
|
66
|
+
async function cachedWithRedis<T>(
|
|
67
|
+
key: string,
|
|
68
|
+
fetcher: () => Promise<T>,
|
|
69
|
+
ttlSeconds = 60
|
|
70
|
+
): Promise<T> {
|
|
71
|
+
const cached = await redis.get(key);
|
|
72
|
+
if (cached) return JSON.parse(cached);
|
|
73
|
+
|
|
74
|
+
const result = await fetcher();
|
|
75
|
+
await redis.setex(key, ttlSeconds, JSON.stringify(result));
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Request Batching
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import DataLoader from 'dataloader';
|
|
84
|
+
|
|
85
|
+
const supabaseLoader = new DataLoader<string, any>(
|
|
86
|
+
async (ids) => {
|
|
87
|
+
// Batch fetch from Supabase
|
|
88
|
+
const results = await supabaseClient.batchGet(ids);
|
|
89
|
+
return ids.map(id => results.find(r => r.id === id) || null);
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
maxBatchSize: 100,
|
|
93
|
+
batchScheduleFn: callback => setTimeout(callback, 10),
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Usage - automatically batched
|
|
98
|
+
const [item1, item2, item3] = await Promise.all([
|
|
99
|
+
supabaseLoader.load('id-1'),
|
|
100
|
+
supabaseLoader.load('id-2'),
|
|
101
|
+
supabaseLoader.load('id-3'),
|
|
102
|
+
]);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Connection Optimization
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { Agent } from 'https';
|
|
109
|
+
|
|
110
|
+
// Keep-alive connection pooling
|
|
111
|
+
const agent = new Agent({
|
|
112
|
+
keepAlive: true,
|
|
113
|
+
maxSockets: 15,
|
|
114
|
+
maxFreeSockets: 5,
|
|
115
|
+
timeout: 30000,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const client = new SupabaseClient({
|
|
119
|
+
apiKey: process.env.SUPABASE_API_KEY!,
|
|
120
|
+
httpAgent: agent,
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Pagination Optimization
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
async function* paginatedSupabaseList<T>(
|
|
128
|
+
fetcher: (cursor?: string) => Promise<{ data: T[]; nextCursor?: string }>
|
|
129
|
+
): AsyncGenerator<T> {
|
|
130
|
+
let cursor: string | undefined;
|
|
131
|
+
|
|
132
|
+
do {
|
|
133
|
+
const { data, nextCursor } = await fetcher(cursor);
|
|
134
|
+
for (const item of data) {
|
|
135
|
+
yield item;
|
|
136
|
+
}
|
|
137
|
+
cursor = nextCursor;
|
|
138
|
+
} while (cursor);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Usage
|
|
142
|
+
for await (const item of paginatedSupabaseList(cursor =>
|
|
143
|
+
supabaseClient.list({ cursor, limit: 100 })
|
|
144
|
+
)) {
|
|
145
|
+
await process(item);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Performance Monitoring
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
async function measuredSupabaseCall<T>(
|
|
153
|
+
operation: string,
|
|
154
|
+
fn: () => Promise<T>
|
|
155
|
+
): Promise<T> {
|
|
156
|
+
const start = performance.now();
|
|
157
|
+
try {
|
|
158
|
+
const result = await fn();
|
|
159
|
+
const duration = performance.now() - start;
|
|
160
|
+
console.log({ operation, duration, status: 'success' });
|
|
161
|
+
return result;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
const duration = performance.now() - start;
|
|
164
|
+
console.error({ operation, duration, status: 'error', error });
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Instructions
|
|
171
|
+
|
|
172
|
+
### Step 1: Establish Baseline
|
|
173
|
+
Measure current latency for critical Supabase operations.
|
|
174
|
+
|
|
175
|
+
### Step 2: Implement Caching
|
|
176
|
+
Add response caching for frequently accessed data.
|
|
177
|
+
|
|
178
|
+
### Step 3: Enable Batching
|
|
179
|
+
Use DataLoader or similar for automatic request batching.
|
|
180
|
+
|
|
181
|
+
### Step 4: Optimize Connections
|
|
182
|
+
Configure connection pooling with keep-alive.
|
|
183
|
+
|
|
184
|
+
## Output
|
|
185
|
+
- Reduced API latency
|
|
186
|
+
- Caching layer implemented
|
|
187
|
+
- Request batching enabled
|
|
188
|
+
- Connection pooling configured
|
|
189
|
+
|
|
190
|
+
## Error Handling
|
|
191
|
+
| Issue | Cause | Solution |
|
|
192
|
+
|-------|-------|----------|
|
|
193
|
+
| Cache miss storm | TTL expired | Use stale-while-revalidate |
|
|
194
|
+
| Batch timeout | Too many items | Reduce batch size |
|
|
195
|
+
| Connection exhausted | No pooling | Configure max sockets |
|
|
196
|
+
| Memory pressure | Cache too large | Set max cache entries |
|
|
197
|
+
|
|
198
|
+
## Examples
|
|
199
|
+
|
|
200
|
+
### Quick Performance Wrapper
|
|
201
|
+
```typescript
|
|
202
|
+
const withPerformance = <T>(name: string, fn: () => Promise<T>) =>
|
|
203
|
+
measuredSupabaseCall(name, () =>
|
|
204
|
+
cachedSupabaseRequest(`cache:${name}`, fn)
|
|
205
|
+
);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Resources
|
|
209
|
+
- [Supabase Performance Guide](https://supabase.com/docs/performance)
|
|
210
|
+
- [DataLoader Documentation](https://github.com/graphql/dataloader)
|
|
211
|
+
- [LRU Cache Documentation](https://github.com/isaacs/node-lru-cache)
|
|
212
|
+
|
|
213
|
+
## Next Steps
|
|
214
|
+
For cost optimization, see `supabase-cost-tuning`.
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-policy-guardrails
|
|
3
|
+
description: |
|
|
4
|
+
Implement Supabase lint rules, policy enforcement, and automated guardrails.
|
|
5
|
+
Use when setting up code quality rules for Supabase integrations, implementing
|
|
6
|
+
pre-commit hooks, or configuring CI policy checks for Supabase best practices.
|
|
7
|
+
Trigger with phrases like "supabase policy", "supabase lint",
|
|
8
|
+
"supabase guardrails", "supabase best practices check", "supabase eslint".
|
|
9
|
+
allowed-tools: Read, Write, Edit, Bash(npx:*)
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Policy & Guardrails
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Automated policy enforcement and guardrails for Supabase integrations.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- ESLint configured in project
|
|
22
|
+
- Pre-commit hooks infrastructure
|
|
23
|
+
- CI/CD pipeline with policy checks
|
|
24
|
+
- TypeScript for type enforcement
|
|
25
|
+
|
|
26
|
+
## ESLint Rules
|
|
27
|
+
|
|
28
|
+
### Custom Supabase Plugin
|
|
29
|
+
```javascript
|
|
30
|
+
// eslint-plugin-supabase/rules/no-hardcoded-keys.js
|
|
31
|
+
module.exports = {
|
|
32
|
+
meta: {
|
|
33
|
+
type: 'problem',
|
|
34
|
+
docs: {
|
|
35
|
+
description: 'Disallow hardcoded Supabase API keys',
|
|
36
|
+
},
|
|
37
|
+
fixable: 'code',
|
|
38
|
+
},
|
|
39
|
+
create(context) {
|
|
40
|
+
return {
|
|
41
|
+
Literal(node) {
|
|
42
|
+
if (typeof node.value === 'string') {
|
|
43
|
+
if (node.value.match(/^sk_(live|test)_[a-zA-Z0-9]{24,}/)) {
|
|
44
|
+
context.report({
|
|
45
|
+
node,
|
|
46
|
+
message: 'Hardcoded Supabase API key detected',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### ESLint Configuration
|
|
57
|
+
```javascript
|
|
58
|
+
// .eslintrc.js
|
|
59
|
+
module.exports = {
|
|
60
|
+
plugins: ['supabase'],
|
|
61
|
+
rules: {
|
|
62
|
+
'supabase/no-hardcoded-keys': 'error',
|
|
63
|
+
'supabase/require-error-handling': 'warn',
|
|
64
|
+
'supabase/use-typed-client': 'warn',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Pre-Commit Hooks
|
|
70
|
+
|
|
71
|
+
```yaml
|
|
72
|
+
# .pre-commit-config.yaml
|
|
73
|
+
repos:
|
|
74
|
+
- repo: local
|
|
75
|
+
hooks:
|
|
76
|
+
- id: supabase-secrets-check
|
|
77
|
+
name: Check for Supabase secrets
|
|
78
|
+
entry: bash -c 'git diff --cached --name-only | xargs grep -l "sk_live_" && exit 1 || exit 0'
|
|
79
|
+
language: system
|
|
80
|
+
pass_filenames: false
|
|
81
|
+
|
|
82
|
+
- id: supabase-config-validate
|
|
83
|
+
name: Validate Supabase configuration
|
|
84
|
+
entry: node scripts/validate-supabase-config.js
|
|
85
|
+
language: node
|
|
86
|
+
files: '\.supabase\.json$'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## TypeScript Strict Patterns
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// Enforce typed configuration
|
|
93
|
+
interface SupabaseStrictConfig {
|
|
94
|
+
apiKey: string; // Required
|
|
95
|
+
environment: 'development' | 'staging' | 'production'; // Enum
|
|
96
|
+
timeout: number; // Required number, not optional
|
|
97
|
+
retries: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Disallow any in Supabase code
|
|
101
|
+
// @ts-expect-error - Using any is forbidden
|
|
102
|
+
const client = new Client({ apiKey: any });
|
|
103
|
+
|
|
104
|
+
// Prefer this
|
|
105
|
+
const client = new SupabaseClient(config satisfies SupabaseStrictConfig);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Architecture Decision Records
|
|
109
|
+
|
|
110
|
+
### ADR Template
|
|
111
|
+
```markdown
|
|
112
|
+
# ADR-001: Supabase Client Initialization
|
|
113
|
+
|
|
114
|
+
## Status
|
|
115
|
+
Accepted
|
|
116
|
+
|
|
117
|
+
## Context
|
|
118
|
+
We need to decide how to initialize the Supabase client across our application.
|
|
119
|
+
|
|
120
|
+
## Decision
|
|
121
|
+
We will use the singleton pattern with lazy initialization.
|
|
122
|
+
|
|
123
|
+
## Consequences
|
|
124
|
+
- Pro: Single client instance, connection reuse
|
|
125
|
+
- Pro: Easy to mock in tests
|
|
126
|
+
- Con: Global state requires careful lifecycle management
|
|
127
|
+
|
|
128
|
+
## Enforcement
|
|
129
|
+
- ESLint rule: supabase/use-singleton-client
|
|
130
|
+
- CI check: grep for "new SupabaseClient(" outside allowed files
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Policy-as-Code (OPA)
|
|
134
|
+
|
|
135
|
+
```rego
|
|
136
|
+
# supabase-policy.rego
|
|
137
|
+
package supabase
|
|
138
|
+
|
|
139
|
+
# Deny production API keys in non-production environments
|
|
140
|
+
deny[msg] {
|
|
141
|
+
input.environment != "production"
|
|
142
|
+
startswith(input.apiKey, "sk_live_")
|
|
143
|
+
msg := "Production API keys not allowed in non-production environment"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# Require minimum timeout
|
|
147
|
+
deny[msg] {
|
|
148
|
+
input.timeout < 10000
|
|
149
|
+
msg := sprintf("Timeout too low: %d < 10000ms minimum", [input.timeout])
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Require retry configuration
|
|
153
|
+
deny[msg] {
|
|
154
|
+
not input.retries
|
|
155
|
+
msg := "Retry configuration is required"
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## CI Policy Checks
|
|
160
|
+
|
|
161
|
+
```yaml
|
|
162
|
+
# .github/workflows/supabase-policy.yml
|
|
163
|
+
name: Supabase Policy Check
|
|
164
|
+
|
|
165
|
+
on: [push, pull_request]
|
|
166
|
+
|
|
167
|
+
jobs:
|
|
168
|
+
policy:
|
|
169
|
+
runs-on: ubuntu-latest
|
|
170
|
+
steps:
|
|
171
|
+
- uses: actions/checkout@v4
|
|
172
|
+
|
|
173
|
+
- name: Check for hardcoded secrets
|
|
174
|
+
run: |
|
|
175
|
+
if grep -rE "sk_(live|test)_[a-zA-Z0-9]{24,}" --include="*.ts" --include="*.js" .; then
|
|
176
|
+
echo "ERROR: Hardcoded Supabase keys found"
|
|
177
|
+
exit 1
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
- name: Validate configuration schema
|
|
181
|
+
run: |
|
|
182
|
+
npx ajv validate -s supabase-config.schema.json -d config/supabase/*.json
|
|
183
|
+
|
|
184
|
+
- name: Run ESLint Supabase rules
|
|
185
|
+
run: npx eslint --plugin supabase --rule 'supabase/no-hardcoded-keys: error' src/
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Runtime Guardrails
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Prevent dangerous operations in production
|
|
192
|
+
const BLOCKED_IN_PROD = ['deleteAll', 'resetData', 'migrateDown'];
|
|
193
|
+
|
|
194
|
+
function guardSupabaseOperation(operation: string): void {
|
|
195
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
196
|
+
|
|
197
|
+
if (isProd && BLOCKED_IN_PROD.includes(operation)) {
|
|
198
|
+
throw new Error(`Operation '${operation}' blocked in production`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Rate limit protection
|
|
203
|
+
function guardRateLimits(requestsInWindow: number): void {
|
|
204
|
+
const limit = parseInt(process.env.SUPABASE_RATE_LIMIT || '100');
|
|
205
|
+
|
|
206
|
+
if (requestsInWindow > limit * 0.9) {
|
|
207
|
+
console.warn('Approaching Supabase rate limit');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (requestsInWindow >= limit) {
|
|
211
|
+
throw new Error('Supabase rate limit exceeded - request blocked');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Instructions
|
|
217
|
+
|
|
218
|
+
### Step 1: Create ESLint Rules
|
|
219
|
+
Implement custom lint rules for Supabase patterns.
|
|
220
|
+
|
|
221
|
+
### Step 2: Configure Pre-Commit Hooks
|
|
222
|
+
Set up hooks to catch issues before commit.
|
|
223
|
+
|
|
224
|
+
### Step 3: Add CI Policy Checks
|
|
225
|
+
Implement policy-as-code in CI pipeline.
|
|
226
|
+
|
|
227
|
+
### Step 4: Enable Runtime Guardrails
|
|
228
|
+
Add production safeguards for dangerous operations.
|
|
229
|
+
|
|
230
|
+
## Output
|
|
231
|
+
- ESLint plugin with Supabase rules
|
|
232
|
+
- Pre-commit hooks blocking secrets
|
|
233
|
+
- CI policy checks passing
|
|
234
|
+
- Runtime guardrails active
|
|
235
|
+
|
|
236
|
+
## Error Handling
|
|
237
|
+
| Issue | Cause | Solution |
|
|
238
|
+
|-------|-------|----------|
|
|
239
|
+
| ESLint rule not firing | Wrong config | Check plugin registration |
|
|
240
|
+
| Pre-commit skipped | --no-verify | Enforce in CI |
|
|
241
|
+
| Policy false positive | Regex too broad | Narrow pattern match |
|
|
242
|
+
| Guardrail triggered | Actual issue | Fix or whitelist |
|
|
243
|
+
|
|
244
|
+
## Examples
|
|
245
|
+
|
|
246
|
+
### Quick ESLint Check
|
|
247
|
+
```bash
|
|
248
|
+
npx eslint --plugin supabase --rule 'supabase/no-hardcoded-keys: error' src/
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Resources
|
|
252
|
+
- [ESLint Plugin Development](https://eslint.org/docs/latest/extend/plugins)
|
|
253
|
+
- [Pre-commit Framework](https://pre-commit.com/)
|
|
254
|
+
- [Open Policy Agent](https://www.openpolicyagent.org/)
|
|
255
|
+
|
|
256
|
+
## Next Steps
|
|
257
|
+
For architecture blueprints, see `supabase-architecture-variants`.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-prod-checklist
|
|
3
|
+
description: |
|
|
4
|
+
Execute Supabase production deployment checklist and rollback procedures.
|
|
5
|
+
Use when deploying Supabase integrations to production, preparing for launch,
|
|
6
|
+
or implementing go-live procedures.
|
|
7
|
+
Trigger with phrases like "supabase production", "deploy supabase",
|
|
8
|
+
"supabase go-live", "supabase launch checklist".
|
|
9
|
+
allowed-tools: Read, Bash(kubectl:*), Bash(curl:*), Grep
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Production Checklist
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Complete checklist for deploying Supabase integrations to production.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Staging environment tested and verified
|
|
22
|
+
- Production API keys available
|
|
23
|
+
- Deployment pipeline configured
|
|
24
|
+
- Monitoring and alerting ready
|
|
25
|
+
|
|
26
|
+
## Instructions
|
|
27
|
+
|
|
28
|
+
### Step 1: Pre-Deployment Configuration
|
|
29
|
+
- [ ] Production API keys in secure vault
|
|
30
|
+
- [ ] Environment variables set in deployment platform
|
|
31
|
+
- [ ] API key scopes are minimal (least privilege)
|
|
32
|
+
- [ ] Webhook endpoints configured with HTTPS
|
|
33
|
+
- [ ] Webhook secrets stored securely
|
|
34
|
+
|
|
35
|
+
### Step 2: Code Quality Verification
|
|
36
|
+
- [ ] All tests passing (`npm test`)
|
|
37
|
+
- [ ] No hardcoded credentials
|
|
38
|
+
- [ ] Error handling covers all Supabase error types
|
|
39
|
+
- [ ] Rate limiting/backoff implemented
|
|
40
|
+
- [ ] Logging is production-appropriate
|
|
41
|
+
|
|
42
|
+
### Step 3: Infrastructure Setup
|
|
43
|
+
- [ ] Health check endpoint includes Supabase connectivity
|
|
44
|
+
- [ ] Monitoring/alerting configured
|
|
45
|
+
- [ ] Circuit breaker pattern implemented
|
|
46
|
+
- [ ] Graceful degradation configured
|
|
47
|
+
|
|
48
|
+
### Step 4: Documentation Requirements
|
|
49
|
+
- [ ] Incident runbook created
|
|
50
|
+
- [ ] Key rotation procedure documented
|
|
51
|
+
- [ ] Rollback procedure documented
|
|
52
|
+
- [ ] On-call escalation path defined
|
|
53
|
+
|
|
54
|
+
### Step 5: Deploy with Gradual Rollout
|
|
55
|
+
```bash
|
|
56
|
+
# Pre-flight checks
|
|
57
|
+
curl -f https://staging.example.com/health
|
|
58
|
+
curl -s https://status.supabase.com
|
|
59
|
+
|
|
60
|
+
# Gradual rollout - start with canary (10%)
|
|
61
|
+
kubectl apply -f k8s/production.yaml
|
|
62
|
+
kubectl set image deployment/supabase-integration app=image:new --record
|
|
63
|
+
kubectl rollout pause deployment/supabase-integration
|
|
64
|
+
|
|
65
|
+
# Monitor canary traffic for 10 minutes
|
|
66
|
+
sleep 600
|
|
67
|
+
# Check error rates and latency before continuing
|
|
68
|
+
|
|
69
|
+
# If healthy, continue rollout to 50%
|
|
70
|
+
kubectl rollout resume deployment/supabase-integration
|
|
71
|
+
kubectl rollout pause deployment/supabase-integration
|
|
72
|
+
sleep 300
|
|
73
|
+
|
|
74
|
+
# Complete rollout to 100%
|
|
75
|
+
kubectl rollout resume deployment/supabase-integration
|
|
76
|
+
kubectl rollout status deployment/supabase-integration
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Output
|
|
80
|
+
- Deployed Supabase integration
|
|
81
|
+
- Health checks passing
|
|
82
|
+
- Monitoring active
|
|
83
|
+
- Rollback procedure documented
|
|
84
|
+
|
|
85
|
+
## Error Handling
|
|
86
|
+
| Alert | Condition | Severity |
|
|
87
|
+
|-------|-----------|----------|
|
|
88
|
+
| API Down | 5xx errors > 10/min | P1 |
|
|
89
|
+
| High Latency | p99 > 5000ms | P2 |
|
|
90
|
+
| Rate Limited | 429 errors > 5/min | P2 |
|
|
91
|
+
| Auth Failures | 401/403 errors > 0 | P1 |
|
|
92
|
+
|
|
93
|
+
## Examples
|
|
94
|
+
|
|
95
|
+
### Health Check Implementation
|
|
96
|
+
```typescript
|
|
97
|
+
async function healthCheck(): Promise<{ status: string; supabase: any }> {
|
|
98
|
+
const start = Date.now();
|
|
99
|
+
try {
|
|
100
|
+
await supabaseClient.ping();
|
|
101
|
+
return { status: 'healthy', supabase: { connected: true, latencyMs: Date.now() - start } };
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return { status: 'degraded', supabase: { connected: false, latencyMs: Date.now() - start } };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Immediate Rollback
|
|
109
|
+
```bash
|
|
110
|
+
kubectl rollout undo deployment/supabase-integration
|
|
111
|
+
kubectl rollout status deployment/supabase-integration
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Resources
|
|
115
|
+
- [Supabase Status](https://status.supabase.com)
|
|
116
|
+
- [Supabase Support](https://supabase.com/docs/support)
|
|
117
|
+
|
|
118
|
+
## Next Steps
|
|
119
|
+
For version upgrades, see `supabase-upgrade-migration`.
|