@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,244 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-migration-deep-dive
|
|
3
|
+
description: |
|
|
4
|
+
Execute Supabase major re-architecture and migration strategies with strangler fig pattern.
|
|
5
|
+
Use when migrating to or from Supabase, performing major version upgrades,
|
|
6
|
+
or re-platforming existing integrations to Supabase.
|
|
7
|
+
Trigger with phrases like "migrate supabase", "supabase migration",
|
|
8
|
+
"switch to supabase", "supabase replatform", "supabase upgrade major".
|
|
9
|
+
allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(node:*), Bash(kubectl:*)
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Migration Deep Dive
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Comprehensive guide for migrating to or from Supabase, or major version upgrades.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Current system documentation
|
|
22
|
+
- Supabase SDK installed
|
|
23
|
+
- Feature flag infrastructure
|
|
24
|
+
- Rollback strategy tested
|
|
25
|
+
|
|
26
|
+
## Migration Types
|
|
27
|
+
|
|
28
|
+
| Type | Complexity | Duration | Risk |
|
|
29
|
+
|------|-----------|----------|------|
|
|
30
|
+
| Fresh install | Low | Days | Low |
|
|
31
|
+
| From competitor | Medium | Weeks | Medium |
|
|
32
|
+
| Major version | Medium | Weeks | Medium |
|
|
33
|
+
| Full replatform | High | Months | High |
|
|
34
|
+
|
|
35
|
+
## Pre-Migration Assessment
|
|
36
|
+
|
|
37
|
+
### Step 1: Current State Analysis
|
|
38
|
+
```bash
|
|
39
|
+
# Document current implementation
|
|
40
|
+
find . -name "*.ts" -o -name "*.py" | xargs grep -l "supabase" > supabase-files.txt
|
|
41
|
+
|
|
42
|
+
# Count integration points
|
|
43
|
+
wc -l supabase-files.txt
|
|
44
|
+
|
|
45
|
+
# Identify dependencies
|
|
46
|
+
npm list | grep supabase
|
|
47
|
+
pip freeze | grep supabase
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Step 2: Data Inventory
|
|
51
|
+
```typescript
|
|
52
|
+
interface MigrationInventory {
|
|
53
|
+
dataTypes: string[];
|
|
54
|
+
recordCounts: Record<string, number>;
|
|
55
|
+
dependencies: string[];
|
|
56
|
+
integrationPoints: string[];
|
|
57
|
+
customizations: string[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function assessSupabaseMigration(): Promise<MigrationInventory> {
|
|
61
|
+
return {
|
|
62
|
+
dataTypes: await getDataTypes(),
|
|
63
|
+
recordCounts: await getRecordCounts(),
|
|
64
|
+
dependencies: await analyzeDependencies(),
|
|
65
|
+
integrationPoints: await findIntegrationPoints(),
|
|
66
|
+
customizations: await documentCustomizations(),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Migration Strategy: Strangler Fig Pattern
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
Phase 1: Parallel Run
|
|
75
|
+
┌─────────────┐ ┌─────────────┐
|
|
76
|
+
│ Old │ │ New │
|
|
77
|
+
│ System │ ──▶ │ Supabase │
|
|
78
|
+
│ (100%) │ │ (0%) │
|
|
79
|
+
└─────────────┘ └─────────────┘
|
|
80
|
+
|
|
81
|
+
Phase 2: Gradual Shift
|
|
82
|
+
┌─────────────┐ ┌─────────────┐
|
|
83
|
+
│ Old │ │ New │
|
|
84
|
+
│ (50%) │ ──▶ │ (50%) │
|
|
85
|
+
└─────────────┘ └─────────────┘
|
|
86
|
+
|
|
87
|
+
Phase 3: Complete
|
|
88
|
+
┌─────────────┐ ┌─────────────┐
|
|
89
|
+
│ Old │ │ New │
|
|
90
|
+
│ (0%) │ ──▶ │ (100%) │
|
|
91
|
+
└─────────────┘ └─────────────┘
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Implementation Plan
|
|
95
|
+
|
|
96
|
+
### Phase 1: Setup (Week 1-2)
|
|
97
|
+
```bash
|
|
98
|
+
# Install Supabase SDK
|
|
99
|
+
npm install @supabase/supabase-js
|
|
100
|
+
|
|
101
|
+
# Configure credentials
|
|
102
|
+
cp .env.example .env.supabase
|
|
103
|
+
# Edit with new credentials
|
|
104
|
+
|
|
105
|
+
# Verify connectivity
|
|
106
|
+
node -e "require('@supabase/supabase-js').ping()"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Phase 2: Adapter Layer (Week 3-4)
|
|
110
|
+
```typescript
|
|
111
|
+
// src/adapters/supabase.ts
|
|
112
|
+
interface ServiceAdapter {
|
|
113
|
+
create(data: CreateInput): Promise<Resource>;
|
|
114
|
+
read(id: string): Promise<Resource>;
|
|
115
|
+
update(id: string, data: UpdateInput): Promise<Resource>;
|
|
116
|
+
delete(id: string): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
class SupabaseAdapter implements ServiceAdapter {
|
|
120
|
+
async create(data: CreateInput): Promise<Resource> {
|
|
121
|
+
const supabaseData = this.transform(data);
|
|
122
|
+
return supabaseClient.create(supabaseData);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private transform(data: CreateInput): SupabaseInput {
|
|
126
|
+
// Map from old format to Supabase format
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Phase 3: Data Migration (Week 5-6)
|
|
132
|
+
```typescript
|
|
133
|
+
async function migrateSupabaseData(): Promise<MigrationResult> {
|
|
134
|
+
const batchSize = 100;
|
|
135
|
+
let processed = 0;
|
|
136
|
+
let errors: MigrationError[] = [];
|
|
137
|
+
|
|
138
|
+
for await (const batch of oldSystem.iterateBatches(batchSize)) {
|
|
139
|
+
try {
|
|
140
|
+
const transformed = batch.map(transform);
|
|
141
|
+
await supabaseClient.batchCreate(transformed);
|
|
142
|
+
processed += batch.length;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
errors.push({ batch, error });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Progress update
|
|
148
|
+
console.log(`Migrated ${processed} records`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { processed, errors };
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Phase 4: Traffic Shift (Week 7-8)
|
|
156
|
+
```typescript
|
|
157
|
+
// Feature flag controlled traffic split
|
|
158
|
+
function getServiceAdapter(): ServiceAdapter {
|
|
159
|
+
const supabasePercentage = getFeatureFlag('supabase_migration_percentage');
|
|
160
|
+
|
|
161
|
+
if (Math.random() * 100 < supabasePercentage) {
|
|
162
|
+
return new SupabaseAdapter();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return new LegacyAdapter();
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Rollback Plan
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Immediate rollback
|
|
173
|
+
kubectl set env deployment/app SUPABASE_ENABLED=false
|
|
174
|
+
kubectl rollout restart deployment/app
|
|
175
|
+
|
|
176
|
+
# Data rollback (if needed)
|
|
177
|
+
./scripts/restore-from-backup.sh --date YYYY-MM-DD
|
|
178
|
+
|
|
179
|
+
# Verify rollback
|
|
180
|
+
curl https://app.yourcompany.com/health | jq '.services.supabase'
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Post-Migration Validation
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
async function validateSupabaseMigration(): Promise<ValidationReport> {
|
|
187
|
+
const checks = [
|
|
188
|
+
{ name: 'Data count match', fn: checkDataCounts },
|
|
189
|
+
{ name: 'API functionality', fn: checkApiFunctionality },
|
|
190
|
+
{ name: 'Performance baseline', fn: checkPerformance },
|
|
191
|
+
{ name: 'Error rates', fn: checkErrorRates },
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
const results = await Promise.all(
|
|
195
|
+
checks.map(async c => ({ name: c.name, result: await c.fn() }))
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
return { checks: results, passed: results.every(r => r.result.success) };
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Instructions
|
|
203
|
+
|
|
204
|
+
### Step 1: Assess Current State
|
|
205
|
+
Document existing implementation and data inventory.
|
|
206
|
+
|
|
207
|
+
### Step 2: Build Adapter Layer
|
|
208
|
+
Create abstraction layer for gradual migration.
|
|
209
|
+
|
|
210
|
+
### Step 3: Migrate Data
|
|
211
|
+
Run batch data migration with error handling.
|
|
212
|
+
|
|
213
|
+
### Step 4: Shift Traffic
|
|
214
|
+
Gradually route traffic to new Supabase integration.
|
|
215
|
+
|
|
216
|
+
## Output
|
|
217
|
+
- Migration assessment complete
|
|
218
|
+
- Adapter layer implemented
|
|
219
|
+
- Data migrated successfully
|
|
220
|
+
- Traffic fully shifted to Supabase
|
|
221
|
+
|
|
222
|
+
## Error Handling
|
|
223
|
+
| Issue | Cause | Solution |
|
|
224
|
+
|-------|-------|----------|
|
|
225
|
+
| Data mismatch | Transform errors | Validate transform logic |
|
|
226
|
+
| Performance drop | No caching | Add caching layer |
|
|
227
|
+
| Rollback triggered | Errors spiked | Reduce traffic percentage |
|
|
228
|
+
| Validation failed | Missing data | Check batch processing |
|
|
229
|
+
|
|
230
|
+
## Examples
|
|
231
|
+
|
|
232
|
+
### Quick Migration Status
|
|
233
|
+
```typescript
|
|
234
|
+
const status = await validateSupabaseMigration();
|
|
235
|
+
console.log(`Migration ${status.passed ? 'PASSED' : 'FAILED'}`);
|
|
236
|
+
status.checks.forEach(c => console.log(` ${c.name}: ${c.result.success}`));
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Resources
|
|
240
|
+
- [Strangler Fig Pattern](https://martinfowler.com/bliki/StranglerFigApplication.html)
|
|
241
|
+
- [Supabase Migration Guide](https://supabase.com/docs/migration)
|
|
242
|
+
|
|
243
|
+
## Flagship+ Skills
|
|
244
|
+
For advanced troubleshooting, see `supabase-advanced-troubleshooting`.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-multi-env-setup
|
|
3
|
+
description: |
|
|
4
|
+
Configure Supabase across development, staging, and production environments.
|
|
5
|
+
Use when setting up multi-environment deployments, configuring per-environment secrets,
|
|
6
|
+
or implementing environment-specific Supabase configurations.
|
|
7
|
+
Trigger with phrases like "supabase environments", "supabase staging",
|
|
8
|
+
"supabase dev prod", "supabase environment setup", "supabase config by env".
|
|
9
|
+
allowed-tools: Read, Write, Edit, Bash(aws:*), Bash(gcloud:*), Bash(vault:*)
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Multi-Environment Setup
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Configure Supabase across development, staging, and production environments.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Separate Supabase accounts or API keys per environment
|
|
22
|
+
- Secret management solution (Vault, AWS Secrets Manager, etc.)
|
|
23
|
+
- CI/CD pipeline with environment variables
|
|
24
|
+
- Environment detection in application
|
|
25
|
+
|
|
26
|
+
## Environment Strategy
|
|
27
|
+
|
|
28
|
+
| Environment | Purpose | API Keys | Data |
|
|
29
|
+
|-------------|---------|----------|------|
|
|
30
|
+
| Development | Local dev | Test keys | Sandbox |
|
|
31
|
+
| Staging | Pre-prod validation | Staging keys | Test data |
|
|
32
|
+
| Production | Live traffic | Production keys | Real data |
|
|
33
|
+
|
|
34
|
+
## Configuration Structure
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
config/
|
|
38
|
+
├── supabase/
|
|
39
|
+
│ ├── base.json # Shared config
|
|
40
|
+
│ ├── development.json # Dev overrides
|
|
41
|
+
│ ├── staging.json # Staging overrides
|
|
42
|
+
│ └── production.json # Prod overrides
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### base.json
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"timeout": 30000,
|
|
49
|
+
"retries": 3,
|
|
50
|
+
"cache": {
|
|
51
|
+
"enabled": true,
|
|
52
|
+
"ttlSeconds": 60
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### development.json
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"apiKey": "${SUPABASE_API_KEY}",
|
|
61
|
+
"baseUrl": "https://api-sandbox.supabase.com",
|
|
62
|
+
"debug": true,
|
|
63
|
+
"cache": {
|
|
64
|
+
"enabled": false
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### staging.json
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"apiKey": "${SUPABASE_API_KEY_STAGING}",
|
|
73
|
+
"baseUrl": "https://api-staging.supabase.com",
|
|
74
|
+
"debug": false
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### production.json
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"apiKey": "${SUPABASE_API_KEY_PROD}",
|
|
82
|
+
"baseUrl": "https://api.supabase.com",
|
|
83
|
+
"debug": false,
|
|
84
|
+
"retries": 5
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Environment Detection
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// src/supabase/config.ts
|
|
92
|
+
import baseConfig from '../../config/supabase/base.json';
|
|
93
|
+
|
|
94
|
+
type Environment = 'development' | 'staging' | 'production';
|
|
95
|
+
|
|
96
|
+
function detectEnvironment(): Environment {
|
|
97
|
+
const env = process.env.NODE_ENV || 'development';
|
|
98
|
+
const validEnvs: Environment[] = ['development', 'staging', 'production'];
|
|
99
|
+
return validEnvs.includes(env as Environment)
|
|
100
|
+
? (env as Environment)
|
|
101
|
+
: 'development';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function getSupabaseConfig() {
|
|
105
|
+
const env = detectEnvironment();
|
|
106
|
+
const envConfig = require(`../../config/supabase/${env}.json`);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
...baseConfig,
|
|
110
|
+
...envConfig,
|
|
111
|
+
environment: env,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Secret Management by Environment
|
|
117
|
+
|
|
118
|
+
### Local Development
|
|
119
|
+
```bash
|
|
120
|
+
# .env.local (git-ignored)
|
|
121
|
+
SUPABASE_API_KEY=sk_test_dev_***
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### CI/CD (GitHub Actions)
|
|
125
|
+
```yaml
|
|
126
|
+
env:
|
|
127
|
+
SUPABASE_API_KEY: ${{ secrets.SUPABASE_API_KEY_${{ matrix.environment }} }}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Production (Vault/Secrets Manager)
|
|
131
|
+
```bash
|
|
132
|
+
# AWS Secrets Manager
|
|
133
|
+
aws secretsmanager get-secret-value --secret-id supabase/production/api-key
|
|
134
|
+
|
|
135
|
+
# GCP Secret Manager
|
|
136
|
+
gcloud secrets versions access latest --secret=supabase-api-key
|
|
137
|
+
|
|
138
|
+
# HashiCorp Vault
|
|
139
|
+
vault kv get -field=api_key secret/supabase/production
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Environment Isolation
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// Prevent production operations in non-prod
|
|
146
|
+
function guardProductionOperation(operation: string): void {
|
|
147
|
+
const config = getSupabaseConfig();
|
|
148
|
+
|
|
149
|
+
if (config.environment !== 'production') {
|
|
150
|
+
console.warn(`[supabase] ${operation} blocked in ${config.environment}`);
|
|
151
|
+
throw new Error(`${operation} only allowed in production`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Usage
|
|
156
|
+
async function deleteAllData() {
|
|
157
|
+
guardProductionOperation('deleteAllData');
|
|
158
|
+
// Dangerous operation here
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Feature Flags by Environment
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
const featureFlags: Record<Environment, Record<string, boolean>> = {
|
|
166
|
+
development: {
|
|
167
|
+
newFeature: true,
|
|
168
|
+
betaApi: true,
|
|
169
|
+
},
|
|
170
|
+
staging: {
|
|
171
|
+
newFeature: true,
|
|
172
|
+
betaApi: false,
|
|
173
|
+
},
|
|
174
|
+
production: {
|
|
175
|
+
newFeature: false,
|
|
176
|
+
betaApi: false,
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Instructions
|
|
182
|
+
|
|
183
|
+
### Step 1: Create Config Structure
|
|
184
|
+
Set up the base and per-environment configuration files.
|
|
185
|
+
|
|
186
|
+
### Step 2: Implement Environment Detection
|
|
187
|
+
Add logic to detect and load environment-specific config.
|
|
188
|
+
|
|
189
|
+
### Step 3: Configure Secrets
|
|
190
|
+
Store API keys securely using your secret management solution.
|
|
191
|
+
|
|
192
|
+
### Step 4: Add Environment Guards
|
|
193
|
+
Implement safeguards for production-only operations.
|
|
194
|
+
|
|
195
|
+
## Output
|
|
196
|
+
- Multi-environment config structure
|
|
197
|
+
- Environment detection logic
|
|
198
|
+
- Secure secret management
|
|
199
|
+
- Production safeguards enabled
|
|
200
|
+
|
|
201
|
+
## Error Handling
|
|
202
|
+
| Issue | Cause | Solution |
|
|
203
|
+
|-------|-------|----------|
|
|
204
|
+
| Wrong environment | Missing NODE_ENV | Set environment variable |
|
|
205
|
+
| Secret not found | Wrong secret path | Verify secret manager config |
|
|
206
|
+
| Config merge fails | Invalid JSON | Validate config files |
|
|
207
|
+
| Production guard triggered | Wrong environment | Check NODE_ENV value |
|
|
208
|
+
|
|
209
|
+
## Examples
|
|
210
|
+
|
|
211
|
+
### Quick Environment Check
|
|
212
|
+
```typescript
|
|
213
|
+
const env = getSupabaseConfig();
|
|
214
|
+
console.log(`Running in ${env.environment} with ${env.baseUrl}`);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Resources
|
|
218
|
+
- [Supabase Environments Guide](https://supabase.com/docs/environments)
|
|
219
|
+
- [12-Factor App Config](https://12factor.net/config)
|
|
220
|
+
|
|
221
|
+
## Next Steps
|
|
222
|
+
For observability setup, see `supabase-observability`.
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-observability
|
|
3
|
+
description: |
|
|
4
|
+
Set up comprehensive observability for Supabase integrations with metrics, traces, and alerts.
|
|
5
|
+
Use when implementing monitoring for Supabase operations, setting up dashboards,
|
|
6
|
+
or configuring alerting for Supabase integration health.
|
|
7
|
+
Trigger with phrases like "supabase monitoring", "supabase metrics",
|
|
8
|
+
"supabase observability", "monitor supabase", "supabase alerts", "supabase tracing".
|
|
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 Observability
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Set up comprehensive observability for Supabase integrations.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Prometheus or compatible metrics backend
|
|
22
|
+
- OpenTelemetry SDK installed
|
|
23
|
+
- Grafana or similar dashboarding tool
|
|
24
|
+
- AlertManager configured
|
|
25
|
+
|
|
26
|
+
## Metrics Collection
|
|
27
|
+
|
|
28
|
+
### Key Metrics
|
|
29
|
+
| Metric | Type | Description |
|
|
30
|
+
|--------|------|-------------|
|
|
31
|
+
| `supabase_requests_total` | Counter | Total API requests |
|
|
32
|
+
| `supabase_request_duration_seconds` | Histogram | Request latency |
|
|
33
|
+
| `supabase_errors_total` | Counter | Error count by type |
|
|
34
|
+
| `supabase_rate_limit_remaining` | Gauge | Rate limit headroom |
|
|
35
|
+
|
|
36
|
+
### Prometheus Metrics
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { Registry, Counter, Histogram, Gauge } from 'prom-client';
|
|
40
|
+
|
|
41
|
+
const registry = new Registry();
|
|
42
|
+
|
|
43
|
+
const requestCounter = new Counter({
|
|
44
|
+
name: 'supabase_requests_total',
|
|
45
|
+
help: 'Total Supabase API requests',
|
|
46
|
+
labelNames: ['method', 'status'],
|
|
47
|
+
registers: [registry],
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const requestDuration = new Histogram({
|
|
51
|
+
name: 'supabase_request_duration_seconds',
|
|
52
|
+
help: 'Supabase request duration',
|
|
53
|
+
labelNames: ['method'],
|
|
54
|
+
buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
|
|
55
|
+
registers: [registry],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const errorCounter = new Counter({
|
|
59
|
+
name: 'supabase_errors_total',
|
|
60
|
+
help: 'Supabase errors by type',
|
|
61
|
+
labelNames: ['error_type'],
|
|
62
|
+
registers: [registry],
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Instrumented Client
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
async function instrumentedRequest<T>(
|
|
70
|
+
method: string,
|
|
71
|
+
operation: () => Promise<T>
|
|
72
|
+
): Promise<T> {
|
|
73
|
+
const timer = requestDuration.startTimer({ method });
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const result = await operation();
|
|
77
|
+
requestCounter.inc({ method, status: 'success' });
|
|
78
|
+
return result;
|
|
79
|
+
} catch (error: any) {
|
|
80
|
+
requestCounter.inc({ method, status: 'error' });
|
|
81
|
+
errorCounter.inc({ error_type: error.code || 'unknown' });
|
|
82
|
+
throw error;
|
|
83
|
+
} finally {
|
|
84
|
+
timer();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Distributed Tracing
|
|
90
|
+
|
|
91
|
+
### OpenTelemetry Setup
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { trace, SpanStatusCode } from '@opentelemetry/api';
|
|
95
|
+
|
|
96
|
+
const tracer = trace.getTracer('supabase-client');
|
|
97
|
+
|
|
98
|
+
async function tracedSupabaseCall<T>(
|
|
99
|
+
operationName: string,
|
|
100
|
+
operation: () => Promise<T>
|
|
101
|
+
): Promise<T> {
|
|
102
|
+
return tracer.startActiveSpan(`supabase.${operationName}`, async (span) => {
|
|
103
|
+
try {
|
|
104
|
+
const result = await operation();
|
|
105
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
106
|
+
return result;
|
|
107
|
+
} catch (error: any) {
|
|
108
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
|
|
109
|
+
span.recordException(error);
|
|
110
|
+
throw error;
|
|
111
|
+
} finally {
|
|
112
|
+
span.end();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Logging Strategy
|
|
119
|
+
|
|
120
|
+
### Structured Logging
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import pino from 'pino';
|
|
124
|
+
|
|
125
|
+
const logger = pino({
|
|
126
|
+
name: 'supabase',
|
|
127
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
function logSupabaseOperation(
|
|
131
|
+
operation: string,
|
|
132
|
+
data: Record<string, any>,
|
|
133
|
+
duration: number
|
|
134
|
+
) {
|
|
135
|
+
logger.info({
|
|
136
|
+
service: 'supabase',
|
|
137
|
+
operation,
|
|
138
|
+
duration_ms: duration,
|
|
139
|
+
...data,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Alert Configuration
|
|
145
|
+
|
|
146
|
+
### Prometheus AlertManager Rules
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
# supabase_alerts.yaml
|
|
150
|
+
groups:
|
|
151
|
+
- name: supabase_alerts
|
|
152
|
+
rules:
|
|
153
|
+
- alert: SupabaseHighErrorRate
|
|
154
|
+
expr: |
|
|
155
|
+
rate(supabase_errors_total[5m]) /
|
|
156
|
+
rate(supabase_requests_total[5m]) > 0.05
|
|
157
|
+
for: 5m
|
|
158
|
+
labels:
|
|
159
|
+
severity: warning
|
|
160
|
+
annotations:
|
|
161
|
+
summary: "Supabase error rate > 5%"
|
|
162
|
+
|
|
163
|
+
- alert: SupabaseHighLatency
|
|
164
|
+
expr: |
|
|
165
|
+
histogram_quantile(0.95,
|
|
166
|
+
rate(supabase_request_duration_seconds_bucket[5m])
|
|
167
|
+
) > 2
|
|
168
|
+
for: 5m
|
|
169
|
+
labels:
|
|
170
|
+
severity: warning
|
|
171
|
+
annotations:
|
|
172
|
+
summary: "Supabase P95 latency > 2s"
|
|
173
|
+
|
|
174
|
+
- alert: SupabaseDown
|
|
175
|
+
expr: up{job="supabase"} == 0
|
|
176
|
+
for: 1m
|
|
177
|
+
labels:
|
|
178
|
+
severity: critical
|
|
179
|
+
annotations:
|
|
180
|
+
summary: "Supabase integration is down"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Dashboard
|
|
184
|
+
|
|
185
|
+
### Grafana Panel Queries
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"panels": [
|
|
190
|
+
{
|
|
191
|
+
"title": "Supabase Request Rate",
|
|
192
|
+
"targets": [{
|
|
193
|
+
"expr": "rate(supabase_requests_total[5m])"
|
|
194
|
+
}]
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"title": "Supabase Latency P50/P95/P99",
|
|
198
|
+
"targets": [{
|
|
199
|
+
"expr": "histogram_quantile(0.5, rate(supabase_request_duration_seconds_bucket[5m]))"
|
|
200
|
+
}]
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Instructions
|
|
207
|
+
|
|
208
|
+
### Step 1: Set Up Metrics Collection
|
|
209
|
+
Implement Prometheus counters, histograms, and gauges for key operations.
|
|
210
|
+
|
|
211
|
+
### Step 2: Add Distributed Tracing
|
|
212
|
+
Integrate OpenTelemetry for end-to-end request tracing.
|
|
213
|
+
|
|
214
|
+
### Step 3: Configure Structured Logging
|
|
215
|
+
Set up JSON logging with consistent field names.
|
|
216
|
+
|
|
217
|
+
### Step 4: Create Alert Rules
|
|
218
|
+
Define Prometheus alerting rules for error rates and latency.
|
|
219
|
+
|
|
220
|
+
## Output
|
|
221
|
+
- Metrics collection enabled
|
|
222
|
+
- Distributed tracing configured
|
|
223
|
+
- Structured logging implemented
|
|
224
|
+
- Alert rules deployed
|
|
225
|
+
|
|
226
|
+
## Error Handling
|
|
227
|
+
| Issue | Cause | Solution |
|
|
228
|
+
|-------|-------|----------|
|
|
229
|
+
| Missing metrics | No instrumentation | Wrap client calls |
|
|
230
|
+
| Trace gaps | Missing propagation | Check context headers |
|
|
231
|
+
| Alert storms | Wrong thresholds | Tune alert rules |
|
|
232
|
+
| High cardinality | Too many labels | Reduce label values |
|
|
233
|
+
|
|
234
|
+
## Examples
|
|
235
|
+
|
|
236
|
+
### Quick Metrics Endpoint
|
|
237
|
+
```typescript
|
|
238
|
+
app.get('/metrics', async (req, res) => {
|
|
239
|
+
res.set('Content-Type', registry.contentType);
|
|
240
|
+
res.send(await registry.metrics());
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Resources
|
|
245
|
+
- [Prometheus Best Practices](https://prometheus.io/docs/practices/naming/)
|
|
246
|
+
- [OpenTelemetry Documentation](https://opentelemetry.io/docs/)
|
|
247
|
+
- [Supabase Observability Guide](https://supabase.com/docs/observability)
|
|
248
|
+
|
|
249
|
+
## Next Steps
|
|
250
|
+
For incident response, see `supabase-incident-runbook`.
|