@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,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-schema-from-requirements
|
|
3
|
+
description: |
|
|
4
|
+
Execute Supabase primary workflow: Schema from Requirements.
|
|
5
|
+
Use when Starting a new project with defined data requirements,
|
|
6
|
+
Refactoring an existing schema based on new features, or Creating migrations from specification documents.
|
|
7
|
+
Trigger with phrases like "supabase schema from requirements",
|
|
8
|
+
"generate database schema with supabase".
|
|
9
|
+
allowed-tools: Read, Write, Edit, Bash(npm:*), Grep
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Schema from Requirements
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Generate Supabase database schema from natural language requirements.
|
|
19
|
+
This is the primary workflow for starting new Supabase projects.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## Prerequisites
|
|
23
|
+
- Completed `supabase-install-auth` setup
|
|
24
|
+
- Understanding of Supabase core concepts
|
|
25
|
+
- Valid API credentials configured
|
|
26
|
+
|
|
27
|
+
## Instructions
|
|
28
|
+
|
|
29
|
+
### Step 1: Initialize
|
|
30
|
+
```typescript
|
|
31
|
+
// Step 1 implementation
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Step 2: Execute
|
|
35
|
+
```typescript
|
|
36
|
+
// Step 2 implementation
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Step 3: Finalize
|
|
40
|
+
```typescript
|
|
41
|
+
// Step 3 implementation
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Output
|
|
45
|
+
- Completed Schema from Requirements execution
|
|
46
|
+
- Expected results from Supabase API
|
|
47
|
+
- Success confirmation or error details
|
|
48
|
+
|
|
49
|
+
## Error Handling
|
|
50
|
+
| Error | Cause | Solution |
|
|
51
|
+
|-------|-------|----------|
|
|
52
|
+
| Error 1 | Cause | Solution |
|
|
53
|
+
| Error 2 | Cause | Solution |
|
|
54
|
+
|
|
55
|
+
## Examples
|
|
56
|
+
|
|
57
|
+
### Complete Workflow
|
|
58
|
+
```typescript
|
|
59
|
+
// Complete workflow example
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Common Variations
|
|
63
|
+
- Variation 1: Description
|
|
64
|
+
- Variation 2: Description
|
|
65
|
+
|
|
66
|
+
## Resources
|
|
67
|
+
- [Supabase Documentation](https://supabase.com/docs)
|
|
68
|
+
- [Supabase API Reference](https://supabase.com/docs/api)
|
|
69
|
+
|
|
70
|
+
## Next Steps
|
|
71
|
+
For secondary workflow, see `supabase-auth-storage-realtime-core`.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-sdk-patterns
|
|
3
|
+
description: |
|
|
4
|
+
Apply production-ready Supabase SDK patterns for TypeScript and Python.
|
|
5
|
+
Use when implementing Supabase integrations, refactoring SDK usage,
|
|
6
|
+
or establishing team coding standards for Supabase.
|
|
7
|
+
Trigger with phrases like "supabase SDK patterns", "supabase best practices",
|
|
8
|
+
"supabase code patterns", "idiomatic supabase".
|
|
9
|
+
allowed-tools: Read, Write, Edit
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase SDK Patterns
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Production-ready patterns for Supabase SDK usage in TypeScript and Python.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Completed `supabase-install-auth` setup
|
|
22
|
+
- Familiarity with async/await patterns
|
|
23
|
+
- Understanding of error handling best practices
|
|
24
|
+
|
|
25
|
+
## Instructions
|
|
26
|
+
|
|
27
|
+
### Step 1: Implement Singleton Pattern (Recommended)
|
|
28
|
+
```typescript
|
|
29
|
+
// src/supabase/client.ts
|
|
30
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
31
|
+
|
|
32
|
+
let instance: SupabaseClient | null = null;
|
|
33
|
+
|
|
34
|
+
export function getSupabaseClient(): SupabaseClient {
|
|
35
|
+
if (!instance) {
|
|
36
|
+
instance = new SupabaseClient({
|
|
37
|
+
apiKey: process.env.SUPABASE_API_KEY!,
|
|
38
|
+
// Additional options
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return instance;
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Step 2: Add Error Handling Wrapper
|
|
46
|
+
```typescript
|
|
47
|
+
import { SupabaseError } from '@supabase/supabase-js';
|
|
48
|
+
|
|
49
|
+
async function safeSupabaseCall<T>(
|
|
50
|
+
operation: () => Promise<T>
|
|
51
|
+
): Promise<{ data: T | null; error: Error | null }> {
|
|
52
|
+
try {
|
|
53
|
+
const data = await operation();
|
|
54
|
+
return { data, error: null };
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (err instanceof SupabaseError) {
|
|
57
|
+
console.error({
|
|
58
|
+
code: err.code,
|
|
59
|
+
message: err.message,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return { data: null, error: err as Error };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Step 3: Implement Retry Logic
|
|
68
|
+
```typescript
|
|
69
|
+
async function withRetry<T>(
|
|
70
|
+
operation: () => Promise<T>,
|
|
71
|
+
maxRetries = 3,
|
|
72
|
+
backoffMs = 1000
|
|
73
|
+
): Promise<T> {
|
|
74
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
75
|
+
try {
|
|
76
|
+
return await operation();
|
|
77
|
+
} catch (err) {
|
|
78
|
+
if (attempt === maxRetries) throw err;
|
|
79
|
+
const delay = backoffMs * Math.pow(2, attempt - 1);
|
|
80
|
+
await new Promise(r => setTimeout(r, delay));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw new Error('Unreachable');
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Output
|
|
88
|
+
- Type-safe client singleton
|
|
89
|
+
- Robust error handling with structured logging
|
|
90
|
+
- Automatic retry with exponential backoff
|
|
91
|
+
- Runtime validation for API responses
|
|
92
|
+
|
|
93
|
+
## Error Handling
|
|
94
|
+
| Pattern | Use Case | Benefit |
|
|
95
|
+
|---------|----------|---------|
|
|
96
|
+
| Safe wrapper | All API calls | Prevents uncaught exceptions |
|
|
97
|
+
| Retry logic | Transient failures | Improves reliability |
|
|
98
|
+
| Type guards | Response validation | Catches API changes |
|
|
99
|
+
| Logging | All operations | Debugging and monitoring |
|
|
100
|
+
|
|
101
|
+
## Examples
|
|
102
|
+
|
|
103
|
+
### Factory Pattern (Multi-tenant)
|
|
104
|
+
```typescript
|
|
105
|
+
const clients = new Map<string, SupabaseClient>();
|
|
106
|
+
|
|
107
|
+
export function getClientForTenant(tenantId: string): SupabaseClient {
|
|
108
|
+
if (!clients.has(tenantId)) {
|
|
109
|
+
const apiKey = getTenantApiKey(tenantId);
|
|
110
|
+
clients.set(tenantId, new SupabaseClient({ apiKey }));
|
|
111
|
+
}
|
|
112
|
+
return clients.get(tenantId)!;
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Python Context Manager
|
|
117
|
+
```python
|
|
118
|
+
from contextlib import asynccontextmanager
|
|
119
|
+
from supabase import SupabaseClient
|
|
120
|
+
|
|
121
|
+
@asynccontextmanager
|
|
122
|
+
async def get_supabase_client():
|
|
123
|
+
client = SupabaseClient()
|
|
124
|
+
try:
|
|
125
|
+
yield client
|
|
126
|
+
finally:
|
|
127
|
+
await client.close()
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Zod Validation
|
|
131
|
+
```typescript
|
|
132
|
+
import { z } from 'zod';
|
|
133
|
+
|
|
134
|
+
const supabaseResponseSchema = z.object({
|
|
135
|
+
id: z.string(),
|
|
136
|
+
status: z.enum(['active', 'inactive']),
|
|
137
|
+
createdAt: z.string().datetime(),
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Resources
|
|
142
|
+
- [Supabase SDK Reference](https://supabase.com/docs/sdk)
|
|
143
|
+
- [Supabase API Types](https://supabase.com/docs/types)
|
|
144
|
+
- [Zod Documentation](https://zod.dev/)
|
|
145
|
+
|
|
146
|
+
## Next Steps
|
|
147
|
+
Apply patterns in `supabase-core-workflow-a` for real-world usage.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-security-basics
|
|
3
|
+
description: |
|
|
4
|
+
Apply Supabase security best practices for secrets and access control.
|
|
5
|
+
Use when securing API keys, implementing least privilege access,
|
|
6
|
+
or auditing Supabase security configuration.
|
|
7
|
+
Trigger with phrases like "supabase security", "supabase secrets",
|
|
8
|
+
"secure supabase", "supabase API key security".
|
|
9
|
+
allowed-tools: Read, Write, Grep
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Security Basics
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Security best practices for Supabase API keys, tokens, and access control.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Supabase SDK installed
|
|
22
|
+
- Understanding of environment variables
|
|
23
|
+
- Access to Supabase dashboard
|
|
24
|
+
|
|
25
|
+
## Instructions
|
|
26
|
+
|
|
27
|
+
### Step 1: Configure Environment Variables
|
|
28
|
+
```bash
|
|
29
|
+
# .env (NEVER commit to git)
|
|
30
|
+
SUPABASE_API_KEY=sk_live_***
|
|
31
|
+
SUPABASE_SECRET=***
|
|
32
|
+
|
|
33
|
+
# .gitignore
|
|
34
|
+
.env
|
|
35
|
+
.env.local
|
|
36
|
+
.env.*.local
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Step 2: Implement Secret Rotation
|
|
40
|
+
```bash
|
|
41
|
+
# 1. Generate new key in Supabase dashboard
|
|
42
|
+
# 2. Update environment variable
|
|
43
|
+
export SUPABASE_API_KEY="new_key_here"
|
|
44
|
+
|
|
45
|
+
# 3. Verify new key works
|
|
46
|
+
curl -H "Authorization: Bearer ${SUPABASE_API_KEY}" \
|
|
47
|
+
https://api.supabase.com/health
|
|
48
|
+
|
|
49
|
+
# 4. Revoke old key in dashboard
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Step 3: Apply Least Privilege
|
|
53
|
+
| Environment | Recommended Scopes |
|
|
54
|
+
|-------------|-------------------|
|
|
55
|
+
| Development | `read, write` |
|
|
56
|
+
| Staging | `read, write, admin` |
|
|
57
|
+
| Production | `read, write` |
|
|
58
|
+
|
|
59
|
+
## Output
|
|
60
|
+
- Secure API key storage
|
|
61
|
+
- Environment-specific access controls
|
|
62
|
+
- Audit logging enabled
|
|
63
|
+
|
|
64
|
+
## Error Handling
|
|
65
|
+
| Security Issue | Detection | Mitigation |
|
|
66
|
+
|----------------|-----------|------------|
|
|
67
|
+
| Exposed API key | Git scanning | Rotate immediately |
|
|
68
|
+
| Excessive scopes | Audit logs | Reduce permissions |
|
|
69
|
+
| Missing rotation | Key age check | Schedule rotation |
|
|
70
|
+
|
|
71
|
+
## Examples
|
|
72
|
+
|
|
73
|
+
### Service Account Pattern
|
|
74
|
+
```typescript
|
|
75
|
+
const clients = {
|
|
76
|
+
reader: new SupabaseClient({
|
|
77
|
+
apiKey: process.env.SUPABASE_READ_KEY,
|
|
78
|
+
}),
|
|
79
|
+
writer: new SupabaseClient({
|
|
80
|
+
apiKey: process.env.SUPABASE_WRITE_KEY,
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Webhook Signature Verification
|
|
86
|
+
```typescript
|
|
87
|
+
import crypto from 'crypto';
|
|
88
|
+
|
|
89
|
+
function verifyWebhookSignature(
|
|
90
|
+
payload: string, signature: string, secret: string
|
|
91
|
+
): boolean {
|
|
92
|
+
const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');
|
|
93
|
+
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Security Checklist
|
|
98
|
+
- [ ] API keys in environment variables
|
|
99
|
+
- [ ] `.env` files in `.gitignore`
|
|
100
|
+
- [ ] Different keys for dev/staging/prod
|
|
101
|
+
- [ ] Minimal scopes per environment
|
|
102
|
+
- [ ] Webhook signatures validated
|
|
103
|
+
- [ ] Audit logging enabled
|
|
104
|
+
|
|
105
|
+
### Audit Logging
|
|
106
|
+
```typescript
|
|
107
|
+
interface AuditEntry {
|
|
108
|
+
timestamp: Date;
|
|
109
|
+
action: string;
|
|
110
|
+
userId: string;
|
|
111
|
+
resource: string;
|
|
112
|
+
result: 'success' | 'failure';
|
|
113
|
+
metadata?: Record<string, any>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function auditLog(entry: Omit<AuditEntry, 'timestamp'>): Promise<void> {
|
|
117
|
+
const log: AuditEntry = { ...entry, timestamp: new Date() };
|
|
118
|
+
|
|
119
|
+
// Log to Supabase analytics
|
|
120
|
+
await supabaseClient.track('audit', log);
|
|
121
|
+
|
|
122
|
+
// Also log locally for compliance
|
|
123
|
+
console.log('[AUDIT]', JSON.stringify(log));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Usage
|
|
127
|
+
await auditLog({
|
|
128
|
+
action: 'supabase.api.call',
|
|
129
|
+
userId: currentUser.id,
|
|
130
|
+
resource: '/v1/resource',
|
|
131
|
+
result: 'success',
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Resources
|
|
136
|
+
- [Supabase Security Guide](https://supabase.com/docs/security)
|
|
137
|
+
- [Supabase API Scopes](https://supabase.com/docs/scopes)
|
|
138
|
+
|
|
139
|
+
## Next Steps
|
|
140
|
+
For production deployment, see `supabase-prod-checklist`.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-upgrade-migration
|
|
3
|
+
description: |
|
|
4
|
+
Analyze, plan, and execute Supabase SDK upgrades with breaking change detection.
|
|
5
|
+
Use when upgrading Supabase SDK versions, detecting deprecations,
|
|
6
|
+
or migrating to new API versions.
|
|
7
|
+
Trigger with phrases like "upgrade supabase", "supabase migration",
|
|
8
|
+
"supabase breaking changes", "update supabase SDK", "analyze supabase version".
|
|
9
|
+
allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(git:*)
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Upgrade & Migration
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Guide for upgrading Supabase SDK versions and handling breaking changes.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Current Supabase SDK installed
|
|
22
|
+
- Git for version control
|
|
23
|
+
- Test suite available
|
|
24
|
+
- Staging environment
|
|
25
|
+
|
|
26
|
+
## Instructions
|
|
27
|
+
|
|
28
|
+
### Step 1: Check Current Version
|
|
29
|
+
```bash
|
|
30
|
+
npm list @supabase/supabase-js
|
|
31
|
+
npm view @supabase/supabase-js version
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Step 2: Review Changelog
|
|
35
|
+
```bash
|
|
36
|
+
open https://github.com/supabase/sdk/releases
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Step 3: Create Upgrade Branch
|
|
40
|
+
```bash
|
|
41
|
+
git checkout -b upgrade/supabase-sdk-vX.Y.Z
|
|
42
|
+
npm install @supabase/supabase-js@latest
|
|
43
|
+
npm test
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Step 4: Handle Breaking Changes
|
|
47
|
+
Update import statements, configuration, and method signatures as needed.
|
|
48
|
+
|
|
49
|
+
## Output
|
|
50
|
+
- Updated SDK version
|
|
51
|
+
- Fixed breaking changes
|
|
52
|
+
- Passing test suite
|
|
53
|
+
- Documented rollback procedure
|
|
54
|
+
|
|
55
|
+
## Error Handling
|
|
56
|
+
| SDK Version | API Version | Node.js | Breaking Changes |
|
|
57
|
+
|-------------|-------------|---------|------------------|
|
|
58
|
+
| 3.x | 2024-01 | 18+ | Major refactor |
|
|
59
|
+
| 2.x | 2023-06 | 16+ | Auth changes |
|
|
60
|
+
| 1.x | 2022-01 | 14+ | Initial release |
|
|
61
|
+
|
|
62
|
+
## Examples
|
|
63
|
+
|
|
64
|
+
### Import Changes
|
|
65
|
+
```typescript
|
|
66
|
+
// Before (v1.x)
|
|
67
|
+
import { Client } from '@supabase/supabase-js';
|
|
68
|
+
|
|
69
|
+
// After (v2.x)
|
|
70
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Configuration Changes
|
|
74
|
+
```typescript
|
|
75
|
+
// Before (v1.x)
|
|
76
|
+
const client = new Client({ key: 'xxx' });
|
|
77
|
+
|
|
78
|
+
// After (v2.x)
|
|
79
|
+
const client = new SupabaseClient({
|
|
80
|
+
apiKey: 'xxx',
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Rollback Procedure
|
|
85
|
+
```bash
|
|
86
|
+
npm install @supabase/supabase-js@1.x.x --save-exact
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Deprecation Handling
|
|
90
|
+
```typescript
|
|
91
|
+
// Monitor for deprecation warnings in development
|
|
92
|
+
if (process.env.NODE_ENV === 'development') {
|
|
93
|
+
process.on('warning', (warning) => {
|
|
94
|
+
if (warning.name === 'DeprecationWarning') {
|
|
95
|
+
console.warn('[Supabase]', warning.message);
|
|
96
|
+
// Log to tracking system for proactive updates
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Common deprecation patterns to watch for:
|
|
102
|
+
// - Renamed methods: client.oldMethod() -> client.newMethod()
|
|
103
|
+
// - Changed parameters: { key: 'x' } -> { apiKey: 'x' }
|
|
104
|
+
// - Removed features: Check release notes before upgrading
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Resources
|
|
108
|
+
- [Supabase Changelog](https://github.com/supabase/sdk/releases)
|
|
109
|
+
- [Supabase Migration Guide](https://supabase.com/docs/migration)
|
|
110
|
+
|
|
111
|
+
## Next Steps
|
|
112
|
+
For CI integration during upgrades, see `supabase-ci-integration`.
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-webhooks-events
|
|
3
|
+
description: |
|
|
4
|
+
Implement Supabase webhook signature validation and event handling.
|
|
5
|
+
Use when setting up webhook endpoints, implementing signature verification,
|
|
6
|
+
or handling Supabase event notifications securely.
|
|
7
|
+
Trigger with phrases like "supabase webhook", "supabase events",
|
|
8
|
+
"supabase webhook signature", "handle supabase events", "supabase notifications".
|
|
9
|
+
allowed-tools: Read, Write, Edit, Bash(curl:*)
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Webhooks & Events
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Securely handle Supabase webhooks with signature validation and replay protection.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Supabase webhook secret configured
|
|
22
|
+
- HTTPS endpoint accessible from internet
|
|
23
|
+
- Understanding of cryptographic signatures
|
|
24
|
+
- Redis or database for idempotency (optional)
|
|
25
|
+
|
|
26
|
+
## Webhook Endpoint Setup
|
|
27
|
+
|
|
28
|
+
### Express.js
|
|
29
|
+
```typescript
|
|
30
|
+
import express from 'express';
|
|
31
|
+
import crypto from 'crypto';
|
|
32
|
+
|
|
33
|
+
const app = express();
|
|
34
|
+
|
|
35
|
+
// IMPORTANT: Raw body needed for signature verification
|
|
36
|
+
app.post('/webhooks/supabase',
|
|
37
|
+
express.raw({ type: 'application/json' }),
|
|
38
|
+
async (req, res) => {
|
|
39
|
+
const signature = req.headers['x-supabase-signature'] as string;
|
|
40
|
+
const timestamp = req.headers['x-supabase-timestamp'] as string;
|
|
41
|
+
|
|
42
|
+
if (!verifySupabaseSignature(req.body, signature, timestamp)) {
|
|
43
|
+
return res.status(401).json({ error: 'Invalid signature' });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const event = JSON.parse(req.body.toString());
|
|
47
|
+
await handleSupabaseEvent(event);
|
|
48
|
+
|
|
49
|
+
res.status(200).json({ received: true });
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Signature Verification
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
function verifySupabaseSignature(
|
|
58
|
+
payload: Buffer,
|
|
59
|
+
signature: string,
|
|
60
|
+
timestamp: string
|
|
61
|
+
): boolean {
|
|
62
|
+
const secret = process.env.SUPABASE_WEBHOOK_SECRET!;
|
|
63
|
+
|
|
64
|
+
// Reject old timestamps (replay attack protection)
|
|
65
|
+
const timestampAge = Date.now() - parseInt(timestamp) * 1000;
|
|
66
|
+
if (timestampAge > 300000) { // 5 minutes
|
|
67
|
+
console.error('Webhook timestamp too old');
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Compute expected signature
|
|
72
|
+
const signedPayload = `${timestamp}.${payload.toString()}`;
|
|
73
|
+
const expectedSignature = crypto
|
|
74
|
+
.createHmac('sha256', secret)
|
|
75
|
+
.update(signedPayload)
|
|
76
|
+
.digest('hex');
|
|
77
|
+
|
|
78
|
+
// Timing-safe comparison
|
|
79
|
+
return crypto.timingSafeEqual(
|
|
80
|
+
Buffer.from(signature),
|
|
81
|
+
Buffer.from(expectedSignature)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Event Handler Pattern
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
type SupabaseEventType = 'resource.created' | 'resource.updated' | 'resource.deleted';
|
|
90
|
+
|
|
91
|
+
interface SupabaseEvent {
|
|
92
|
+
id: string;
|
|
93
|
+
type: SupabaseEventType;
|
|
94
|
+
data: Record<string, any>;
|
|
95
|
+
created: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const eventHandlers: Record<SupabaseEventType, (data: any) => Promise<void>> = {
|
|
99
|
+
'resource.created': async (data) => { /* handle */ },
|
|
100
|
+
'resource.updated': async (data) => { /* handle */ },
|
|
101
|
+
'resource.deleted': async (data) => { /* handle */ }
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
async function handleSupabaseEvent(event: SupabaseEvent): Promise<void> {
|
|
105
|
+
const handler = eventHandlers[event.type];
|
|
106
|
+
|
|
107
|
+
if (!handler) {
|
|
108
|
+
console.log(`Unhandled event type: ${event.type}`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
await handler(event.data);
|
|
114
|
+
console.log(`Processed ${event.type}: ${event.id}`);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error(`Failed to process ${event.type}: ${event.id}`, error);
|
|
117
|
+
throw error; // Rethrow to trigger retry
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Idempotency Handling
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { Redis } from 'ioredis';
|
|
126
|
+
|
|
127
|
+
const redis = new Redis(process.env.REDIS_URL);
|
|
128
|
+
|
|
129
|
+
async function isEventProcessed(eventId: string): Promise<boolean> {
|
|
130
|
+
const key = `supabase:event:${eventId}`;
|
|
131
|
+
const exists = await redis.exists(key);
|
|
132
|
+
return exists === 1;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function markEventProcessed(eventId: string): Promise<void> {
|
|
136
|
+
const key = `supabase:event:${eventId}`;
|
|
137
|
+
await redis.set(key, '1', 'EX', 86400 * 7); // 7 days TTL
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Webhook Testing
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Use Supabase CLI to send test events
|
|
145
|
+
supabase functions invoke webhook-handler
|
|
146
|
+
|
|
147
|
+
# Or use webhook.site for debugging
|
|
148
|
+
curl -X POST https://webhook.site/your-uuid \
|
|
149
|
+
-H "Content-Type: application/json" \
|
|
150
|
+
-d '{"type": "resource.created", "data": {}}'
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Instructions
|
|
154
|
+
|
|
155
|
+
### Step 1: Register Webhook Endpoint
|
|
156
|
+
Configure your webhook URL in the Supabase dashboard.
|
|
157
|
+
|
|
158
|
+
### Step 2: Implement Signature Verification
|
|
159
|
+
Use the signature verification code to validate incoming webhooks.
|
|
160
|
+
|
|
161
|
+
### Step 3: Handle Events
|
|
162
|
+
Implement handlers for each event type your application needs.
|
|
163
|
+
|
|
164
|
+
### Step 4: Add Idempotency
|
|
165
|
+
Prevent duplicate processing with event ID tracking.
|
|
166
|
+
|
|
167
|
+
## Output
|
|
168
|
+
- Secure webhook endpoint
|
|
169
|
+
- Signature validation enabled
|
|
170
|
+
- Event handlers implemented
|
|
171
|
+
- Replay attack protection active
|
|
172
|
+
|
|
173
|
+
## Error Handling
|
|
174
|
+
| Issue | Cause | Solution |
|
|
175
|
+
|-------|-------|----------|
|
|
176
|
+
| Invalid signature | Wrong secret | Verify webhook secret |
|
|
177
|
+
| Timestamp rejected | Clock drift | Check server time sync |
|
|
178
|
+
| Duplicate events | Missing idempotency | Implement event ID tracking |
|
|
179
|
+
| Handler timeout | Slow processing | Use async queue |
|
|
180
|
+
|
|
181
|
+
## Examples
|
|
182
|
+
|
|
183
|
+
### Testing Webhooks Locally
|
|
184
|
+
```bash
|
|
185
|
+
# Use ngrok to expose local server
|
|
186
|
+
ngrok http 3000
|
|
187
|
+
|
|
188
|
+
# Send test webhook
|
|
189
|
+
curl -X POST https://your-ngrok-url/webhooks/supabase \
|
|
190
|
+
-H "Content-Type: application/json" \
|
|
191
|
+
-d '{"type": "test", "data": {}}'
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Resources
|
|
195
|
+
- [Supabase Webhooks Guide](https://supabase.com/docs/webhooks)
|
|
196
|
+
- [Webhook Security Best Practices](https://supabase.com/docs/webhooks/security)
|
|
197
|
+
|
|
198
|
+
## Next Steps
|
|
199
|
+
For performance optimization, see `supabase-performance-tuning`.
|