@intentsolutionsio/supabase-pack 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +73 -47
  3. package/package.json +4 -4
  4. package/skills/supabase-advanced-troubleshooting/SKILL.md +404 -200
  5. package/skills/supabase-advanced-troubleshooting/references/errors.md +11 -0
  6. package/skills/supabase-advanced-troubleshooting/references/evidence-collection-framework.md +34 -0
  7. package/skills/supabase-advanced-troubleshooting/references/examples.md +11 -0
  8. package/skills/supabase-advanced-troubleshooting/references/rls-edge-functions-realtime.md +363 -0
  9. package/skills/supabase-advanced-troubleshooting/references/systematic-isolation.md +56 -0
  10. package/skills/supabase-advanced-troubleshooting/references/timing-analysis.md +35 -0
  11. package/skills/supabase-architecture-variants/SKILL.md +395 -216
  12. package/skills/supabase-architecture-variants/references/errors.md +11 -0
  13. package/skills/supabase-architecture-variants/references/examples.md +12 -0
  14. package/skills/supabase-architecture-variants/references/serverless-and-multi-tenant.md +251 -0
  15. package/skills/supabase-architecture-variants/references/variant-a-monolith-(simple).md +44 -0
  16. package/skills/supabase-architecture-variants/references/variant-b-service-layer-(moderate).md +72 -0
  17. package/skills/supabase-architecture-variants/references/variant-c-microservice-(complex).md +81 -0
  18. package/skills/supabase-auth-storage-realtime-core/SKILL.md +471 -37
  19. package/skills/supabase-ci-integration/SKILL.md +315 -67
  20. package/skills/supabase-ci-integration/references/errors.md +10 -0
  21. package/skills/supabase-ci-integration/references/examples.md +36 -0
  22. package/skills/supabase-ci-integration/references/implementation.md +54 -0
  23. package/skills/supabase-common-errors/SKILL.md +320 -62
  24. package/skills/supabase-common-errors/references/errors.md +53 -0
  25. package/skills/supabase-common-errors/references/examples.md +23 -0
  26. package/skills/supabase-cost-tuning/SKILL.md +365 -131
  27. package/skills/supabase-cost-tuning/references/cost-estimation.md +34 -0
  28. package/skills/supabase-cost-tuning/references/cost-reduction-strategies.md +40 -0
  29. package/skills/supabase-cost-tuning/references/errors.md +11 -0
  30. package/skills/supabase-cost-tuning/references/examples.md +15 -0
  31. package/skills/supabase-data-handling/SKILL.md +378 -145
  32. package/skills/supabase-data-handling/references/errors.md +11 -0
  33. package/skills/supabase-data-handling/references/examples.md +27 -0
  34. package/skills/supabase-data-handling/references/implementation.md +223 -0
  35. package/skills/supabase-data-handling/references/retention-and-backup.md +221 -0
  36. package/skills/supabase-debug-bundle/SKILL.md +267 -73
  37. package/skills/supabase-debug-bundle/references/errors.md +12 -0
  38. package/skills/supabase-debug-bundle/references/examples.md +24 -0
  39. package/skills/supabase-debug-bundle/references/implementation.md +54 -0
  40. package/skills/supabase-deploy-integration/SKILL.md +258 -147
  41. package/skills/supabase-deploy-integration/references/errors.md +11 -0
  42. package/skills/supabase-deploy-integration/references/examples.md +21 -0
  43. package/skills/supabase-deploy-integration/references/google-cloud-run.md +36 -0
  44. package/skills/supabase-deploy-integration/references/vercel-deployment.md +35 -0
  45. package/skills/supabase-enterprise-rbac/SKILL.md +327 -160
  46. package/skills/supabase-enterprise-rbac/references/api-scoping-and-enforcement.md +255 -0
  47. package/skills/supabase-enterprise-rbac/references/errors.md +11 -0
  48. package/skills/supabase-enterprise-rbac/references/examples.md +12 -0
  49. package/skills/supabase-enterprise-rbac/references/role-implementation.md +33 -0
  50. package/skills/supabase-enterprise-rbac/references/sso-integration.md +35 -0
  51. package/skills/supabase-hello-world/SKILL.md +160 -54
  52. package/skills/supabase-incident-runbook/SKILL.md +453 -131
  53. package/skills/supabase-incident-runbook/references/errors.md +11 -0
  54. package/skills/supabase-incident-runbook/references/examples.md +10 -0
  55. package/skills/supabase-incident-runbook/references/immediate-actions-by-error-type.md +41 -0
  56. package/skills/supabase-install-auth/SKILL.md +186 -50
  57. package/skills/supabase-install-auth/references/examples.md +102 -0
  58. package/skills/supabase-known-pitfalls/SKILL.md +411 -241
  59. package/skills/supabase-known-pitfalls/references/errors.md +11 -0
  60. package/skills/supabase-known-pitfalls/references/examples.md +12 -0
  61. package/skills/supabase-load-scale/SKILL.md +346 -217
  62. package/skills/supabase-load-scale/references/capacity-planning.md +47 -0
  63. package/skills/supabase-load-scale/references/errors.md +11 -0
  64. package/skills/supabase-load-scale/references/examples.md +26 -0
  65. package/skills/supabase-load-scale/references/load-testing-with-k6.md +59 -0
  66. package/skills/supabase-load-scale/references/scaling-patterns.md +65 -0
  67. package/skills/supabase-load-scale/references/table-partitioning.md +263 -0
  68. package/skills/supabase-local-dev-loop/SKILL.md +272 -73
  69. package/skills/supabase-local-dev-loop/references/errors.md +11 -0
  70. package/skills/supabase-local-dev-loop/references/examples.md +21 -0
  71. package/skills/supabase-local-dev-loop/references/implementation.md +60 -0
  72. package/skills/supabase-migration-deep-dive/SKILL.md +338 -177
  73. package/skills/supabase-migration-deep-dive/references/backfill-versioning-rollback.md +258 -0
  74. package/skills/supabase-migration-deep-dive/references/errors.md +11 -0
  75. package/skills/supabase-migration-deep-dive/references/examples.md +12 -0
  76. package/skills/supabase-migration-deep-dive/references/implementation-plan.md +80 -0
  77. package/skills/supabase-migration-deep-dive/references/pre-migration-assessment.md +39 -0
  78. package/skills/supabase-multi-env-setup/SKILL.md +393 -152
  79. package/skills/supabase-multi-env-setup/references/configuration-structure.md +59 -0
  80. package/skills/supabase-multi-env-setup/references/errors.md +11 -0
  81. package/skills/supabase-multi-env-setup/references/examples.md +11 -0
  82. package/skills/supabase-observability/SKILL.md +318 -196
  83. package/skills/supabase-observability/references/alert-configuration.md +40 -0
  84. package/skills/supabase-observability/references/errors.md +11 -0
  85. package/skills/supabase-observability/references/examples.md +13 -0
  86. package/skills/supabase-observability/references/metrics-collection.md +65 -0
  87. package/skills/supabase-performance-tuning/SKILL.md +304 -160
  88. package/skills/supabase-performance-tuning/references/caching-strategy.md +49 -0
  89. package/skills/supabase-performance-tuning/references/errors.md +11 -0
  90. package/skills/supabase-performance-tuning/references/examples.md +13 -0
  91. package/skills/supabase-policy-guardrails/SKILL.md +248 -221
  92. package/skills/supabase-policy-guardrails/references/ci-cost-security.md +484 -0
  93. package/skills/supabase-policy-guardrails/references/errors.md +11 -0
  94. package/skills/supabase-policy-guardrails/references/eslint-rules.md +46 -0
  95. package/skills/supabase-policy-guardrails/references/examples.md +10 -0
  96. package/skills/supabase-prod-checklist/SKILL.md +474 -84
  97. package/skills/supabase-prod-checklist/references/errors.md +63 -0
  98. package/skills/supabase-prod-checklist/references/examples.md +153 -0
  99. package/skills/supabase-prod-checklist/references/implementation.md +113 -0
  100. package/skills/supabase-rate-limits/SKILL.md +311 -98
  101. package/skills/supabase-rate-limits/references/errors.md +11 -0
  102. package/skills/supabase-rate-limits/references/examples.md +46 -0
  103. package/skills/supabase-rate-limits/references/implementation.md +66 -0
  104. package/skills/supabase-reference-architecture/SKILL.md +249 -182
  105. package/skills/supabase-reference-architecture/references/errors.md +29 -0
  106. package/skills/supabase-reference-architecture/references/examples.md +116 -0
  107. package/skills/supabase-reference-architecture/references/key-components.md +244 -0
  108. package/skills/supabase-reference-architecture/references/project-structure.md +109 -0
  109. package/skills/supabase-reliability-patterns/SKILL.md +229 -234
  110. package/skills/supabase-reliability-patterns/references/circuit-breaker.md +36 -0
  111. package/skills/supabase-reliability-patterns/references/dead-letter-queue.md +48 -0
  112. package/skills/supabase-reliability-patterns/references/errors.md +11 -0
  113. package/skills/supabase-reliability-patterns/references/examples.md +11 -0
  114. package/skills/supabase-reliability-patterns/references/idempotency-keys.md +36 -0
  115. package/skills/supabase-reliability-patterns/references/offline-degradation-health-dualwrite.md +489 -0
  116. package/skills/supabase-schema-from-requirements/SKILL.md +373 -34
  117. package/skills/supabase-sdk-patterns/SKILL.md +388 -99
  118. package/skills/supabase-sdk-patterns/references/errors.md +11 -0
  119. package/skills/supabase-sdk-patterns/references/examples.md +45 -0
  120. package/skills/supabase-sdk-patterns/references/implementation.md +67 -0
  121. package/skills/supabase-security-basics/SKILL.md +282 -102
  122. package/skills/supabase-security-basics/references/errors.md +10 -0
  123. package/skills/supabase-security-basics/references/examples.md +70 -0
  124. package/skills/supabase-security-basics/references/implementation.md +39 -0
  125. package/skills/supabase-upgrade-migration/SKILL.md +248 -66
  126. package/skills/supabase-upgrade-migration/references/errors.md +10 -0
  127. package/skills/supabase-upgrade-migration/references/examples.md +51 -0
  128. package/skills/supabase-upgrade-migration/references/implementation.md +29 -0
  129. package/skills/supabase-webhooks-events/SKILL.md +412 -138
  130. package/skills/supabase-webhooks-events/references/errors.md +55 -0
  131. package/skills/supabase-webhooks-events/references/event-handler-pattern.md +106 -0
  132. package/skills/supabase-webhooks-events/references/examples.md +133 -0
  133. package/skills/supabase-webhooks-events/references/signature-verification.md +165 -0
@@ -0,0 +1,65 @@
1
+ # Metrics Collection
2
+
3
+ ## Metrics Collection
4
+
5
+ ### Key Metrics
6
+
7
+ | Metric | Type | Description |
8
+ |--------|------|-------------|
9
+ | `supabase_requests_total` | Counter | Total API requests |
10
+ | `supabase_request_duration_seconds` | Histogram | Request latency |
11
+ | `supabase_errors_total` | Counter | Error count by type |
12
+ | `supabase_rate_limit_remaining` | Gauge | Rate limit headroom |
13
+
14
+ ### Prometheus Metrics
15
+
16
+ ```typescript
17
+ import { Registry, Counter, Histogram, Gauge } from 'prom-client';
18
+
19
+ const registry = new Registry();
20
+
21
+ const requestCounter = new Counter({
22
+ name: 'supabase_requests_total',
23
+ help: 'Total Supabase API requests',
24
+ labelNames: ['method', 'status'],
25
+ registers: [registry],
26
+ });
27
+
28
+ const requestDuration = new Histogram({
29
+ name: 'supabase_request_duration_seconds',
30
+ help: 'Supabase request duration',
31
+ labelNames: ['method'],
32
+ buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
33
+ registers: [registry],
34
+ });
35
+
36
+ const errorCounter = new Counter({
37
+ name: 'supabase_errors_total',
38
+ help: 'Supabase errors by type',
39
+ labelNames: ['error_type'],
40
+ registers: [registry],
41
+ });
42
+ ```
43
+
44
+ ### Instrumented Client
45
+
46
+ ```typescript
47
+ async function instrumentedRequest<T>(
48
+ method: string,
49
+ operation: () => Promise<T>
50
+ ): Promise<T> {
51
+ const timer = requestDuration.startTimer({ method });
52
+
53
+ try {
54
+ const result = await operation();
55
+ requestCounter.inc({ method, status: 'success' });
56
+ return result;
57
+ } catch (error: any) {
58
+ requestCounter.inc({ method, status: 'error' });
59
+ errorCounter.inc({ error_type: error.code || 'unknown' });
60
+ throw error;
61
+ } finally {
62
+ timer();
63
+ }
64
+ }
65
+ ```
@@ -1,214 +1,358 @@
1
1
  ---
2
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
3
+ description: 'Optimize Supabase query performance with indexes, EXPLAIN ANALYZE, connection
4
+ pooling,
5
+
6
+ column selection, pagination, RPC functions, materialized views, and diagnostics.
7
+
8
+ Use when queries are slow, connections are exhausted, response payloads are bloated,
9
+
10
+ or when preparing a Supabase project for production-scale traffic.
11
+
12
+ Trigger with phrases like "supabase performance", "supabase slow queries",
13
+
14
+ "optimize supabase", "supabase index", "supabase connection pool",
15
+
16
+ "supabase pagination", "supabase explain analyze".
17
+
18
+ '
19
+ allowed-tools: Read, Write, Edit, Bash(npx:supabase), Bash(supabase:*), Grep
10
20
  version: 1.0.0
11
21
  license: MIT
12
22
  author: Jeremy Longshore <jeremy@intentsolutions.io>
23
+ tags:
24
+ - saas
25
+ - supabase
26
+ - performance
27
+ - optimization
28
+ - postgres
29
+ compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw
13
30
  ---
14
-
15
31
  # Supabase Performance Tuning
16
32
 
17
33
  ## Overview
18
- Optimize Supabase API performance with caching, batching, and connection pooling.
34
+
35
+ Systematically improve Supabase query and database performance across three layers: PostgreSQL engine (indexes, query plans, materialized views), Supabase infrastructure (Supavisor connection pooling, Edge Functions, read replicas), and client SDK patterns (column selection, pagination, RPC functions). Every technique here is measurable — run `EXPLAIN ANALYZE` before and after to confirm the improvement.
19
36
 
20
37
  ## Prerequisites
21
- - Supabase SDK installed
22
- - Understanding of async patterns
23
- - Redis or in-memory cache available (optional)
24
- - Performance monitoring in place
25
38
 
26
- ## Latency Benchmarks
39
+ - Supabase project (local or hosted) with `@supabase/supabase-js` v2+ installed
40
+ - Supabase CLI installed (`npx supabase --version` to verify)
41
+ - Access to the SQL Editor in the Supabase Dashboard or a direct Postgres connection
42
+ - `pg_stat_statements` extension enabled (Step 1 covers this)
27
43
 
28
- | Operation | P50 | P95 | P99 |
29
- |-----------|-----|-----|-----|
30
- | Select | 15ms | 50ms | 100ms |
31
- | Insert | 25ms | 75ms | 150ms |
32
- | Real-time Subscribe | 50ms | 150ms | 300ms |
44
+ ## Instructions
33
45
 
34
- ## Caching Strategy
46
+ ### Step 1: Diagnose — Find What Is Slow
35
47
 
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
- }
48
+ Start every performance effort with data. Enable `pg_stat_statements` and run the Supabase CLI diagnostics to identify bottlenecks before optimizing.
49
+
50
+ **Enable the stats extension:**
51
+
52
+ ```sql
53
+ CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
58
54
  ```
59
55
 
60
- ### Redis Caching (Distributed)
61
- ```typescript
62
- import Redis from 'ioredis';
56
+ **Find the slowest queries by average execution time:**
57
+
58
+ ```sql
59
+ SELECT
60
+ query,
61
+ calls,
62
+ mean_exec_time::numeric(10,2) AS avg_ms,
63
+ total_exec_time::numeric(10,2) AS total_ms,
64
+ rows
65
+ FROM pg_stat_statements
66
+ ORDER BY mean_exec_time DESC
67
+ LIMIT 10;
68
+ ```
63
69
 
64
- const redis = new Redis(process.env.REDIS_URL);
70
+ **Check index usage and cache hit rates with the Supabase CLI:**
65
71
 
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);
72
+ ```bash
73
+ # Which indexes are actually being used?
74
+ npx supabase inspect db index-usage
73
75
 
74
- const result = await fetcher();
75
- await redis.setex(key, ttlSeconds, JSON.stringify(result));
76
- return result;
77
- }
76
+ # What percentage of queries are served from cache vs disk?
77
+ npx supabase inspect db cache-hit
78
+
79
+ # Tables consuming the most space
80
+ npx supabase inspect db table-sizes
78
81
  ```
79
82
 
80
- ## Request Batching
83
+ **Inspect active connections for pooling issues:**
81
84
 
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
85
+ ```sql
86
+ SELECT state, count(*), max(age(now(), state_change)) AS max_age
87
+ FROM pg_stat_activity
88
+ WHERE datname = current_database()
89
+ GROUP BY state;
90
+ ```
106
91
 
107
- ```typescript
108
- import { Agent } from 'https';
92
+ If `idle` connections exceed your plan's limit or `active` queries show high `max_age`, connection pooling (Step 2) and query optimization (Step 3) are the priority.
93
+
94
+ ### Step 2: Indexes and Query Plans
95
+
96
+ Indexes are the single highest-impact optimization. Use `EXPLAIN ANALYZE` to read query plans, then create targeted indexes.
97
+
98
+ **Read a query plan:**
99
+
100
+ ```sql
101
+ EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
102
+ SELECT * FROM users WHERE email = 'alice@example.com';
103
+ ```
104
+
105
+ Look for `Seq Scan` on large tables — that means no index is being used. After adding an index, the plan should show `Index Scan` or `Index Only Scan`.
106
+
107
+ **Create a basic index:**
109
108
 
110
- // Keep-alive connection pooling
111
- const agent = new Agent({
112
- keepAlive: true,
113
- maxSockets: 15,
114
- maxFreeSockets: 5,
115
- timeout: 30000,
116
- });
109
+ ```sql
110
+ CREATE INDEX idx_users_email ON users(email);
111
+ ```
112
+
113
+ **Create a composite index for multi-column filters:**
114
+
115
+ ```sql
116
+ -- Optimizes: WHERE user_id = ? AND created_at > ? ORDER BY created_at DESC
117
+ CREATE INDEX idx_orders_user_created
118
+ ON orders(user_id, created_at DESC);
119
+ ```
120
+
121
+ **Create a partial index to cover a common filter pattern:**
117
122
 
118
- const client = new SupabaseClient({
119
- apiKey: process.env.SUPABASE_API_KEY!,
120
- httpAgent: agent,
121
- });
123
+ ```sql
124
+ -- Only indexes incomplete todos — much smaller and faster than full-table index
125
+ CREATE INDEX idx_todos_user_incomplete
126
+ ON todos(user_id, inserted_at DESC)
127
+ WHERE is_complete = false;
122
128
  ```
123
129
 
124
- ## Pagination Optimization
130
+ **Find missing indexes on foreign keys (common source of slow JOINs):**
131
+
132
+ ```sql
133
+ SELECT
134
+ tc.table_name,
135
+ kcu.column_name AS fk_column,
136
+ 'CREATE INDEX idx_' || tc.table_name || '_' || kcu.column_name
137
+ || ' ON public.' || tc.table_name || '(' || kcu.column_name || ');' AS fix
138
+ FROM information_schema.table_constraints tc
139
+ JOIN information_schema.key_column_usage kcu
140
+ ON tc.constraint_name = kcu.constraint_name
141
+ LEFT JOIN pg_indexes i
142
+ ON i.tablename = tc.table_name
143
+ AND i.indexdef LIKE '%' || kcu.column_name || '%'
144
+ WHERE tc.constraint_type = 'FOREIGN KEY'
145
+ AND tc.table_schema = 'public'
146
+ AND i.indexname IS NULL;
147
+ ```
148
+
149
+ **Find unused indexes (candidates for removal to reduce write overhead):**
150
+
151
+ ```sql
152
+ SELECT schemaname, relname, indexrelname, idx_scan
153
+ FROM pg_stat_user_indexes
154
+ WHERE idx_scan = 0 AND schemaname = 'public'
155
+ ORDER BY pg_relation_size(indexrelid) DESC;
156
+ ```
157
+
158
+ Always use `CREATE INDEX CONCURRENTLY` on production tables to avoid locking writes during index creation.
159
+
160
+ ### Step 3: Client SDK and Infrastructure Optimization
161
+
162
+ Optimize the Supabase JS client calls, then leverage infrastructure features for scale.
163
+
164
+ **Select only needed columns — avoid `select('*')`:**
125
165
 
126
166
  ```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
- }
167
+ import { createClient } from '@supabase/supabase-js'
140
168
 
141
- // Usage
142
- for await (const item of paginatedSupabaseList(cursor =>
143
- supabaseClient.list({ cursor, limit: 100 })
144
- )) {
145
- await process(item);
146
- }
169
+ const supabase = createClient(
170
+ process.env.SUPABASE_URL!,
171
+ process.env.SUPABASE_ANON_KEY!
172
+ )
173
+
174
+ // BAD: fetches every column, large payloads
175
+ const { data } = await supabase.from('users').select('*')
176
+
177
+ // GOOD: only the columns you need
178
+ const { data } = await supabase.from('users').select('id, name, avatar_url')
147
179
  ```
148
180
 
149
- ## Performance Monitoring
181
+ **Paginate with `.range()` instead of loading all rows:**
150
182
 
151
183
  ```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
- }
184
+ // Page 1: rows 0-49
185
+ const { data: page1 } = await supabase
186
+ .from('products')
187
+ .select('id, name, price')
188
+ .order('created_at', { ascending: false })
189
+ .range(0, 49)
190
+
191
+ // Page 2: rows 50-99
192
+ const { data: page2 } = await supabase
193
+ .from('products')
194
+ .select('id, name, price')
195
+ .order('created_at', { ascending: false })
196
+ .range(50, 99)
168
197
  ```
169
198
 
170
- ## Instructions
199
+ **Use RPC functions to push complex logic to Postgres:**
200
+
201
+ ```sql
202
+ -- Create a server-side function for an expensive aggregation
203
+ CREATE OR REPLACE FUNCTION get_dashboard_stats(org_id uuid)
204
+ RETURNS json AS $$
205
+ SELECT json_build_object(
206
+ 'total_users', (SELECT count(*) FROM users WHERE organization_id = org_id),
207
+ 'active_projects', (SELECT count(*) FROM projects WHERE organization_id = org_id AND status = 'active'),
208
+ 'tasks_completed_30d', (SELECT count(*) FROM tasks t
209
+ JOIN projects p ON p.id = t.project_id
210
+ WHERE p.organization_id = org_id
211
+ AND t.completed_at > now() - interval '30 days')
212
+ );
213
+ $$ LANGUAGE sql STABLE;
214
+ ```
215
+
216
+ ```typescript
217
+ // One network call instead of three separate queries
218
+ const { data } = await supabase.rpc('get_dashboard_stats', {
219
+ org_id: 'your-org-uuid'
220
+ })
221
+ ```
222
+
223
+ **Create materialized views for expensive aggregations:**
224
+
225
+ ```sql
226
+ -- Precompute a leaderboard instead of recalculating on every request
227
+ CREATE MATERIALIZED VIEW leaderboard AS
228
+ SELECT
229
+ u.id,
230
+ u.username,
231
+ count(t.id) AS tasks_completed,
232
+ rank() OVER (ORDER BY count(t.id) DESC) AS rank
233
+ FROM users u
234
+ LEFT JOIN tasks t ON t.assignee_id = u.id AND t.status = 'done'
235
+ GROUP BY u.id, u.username;
236
+
237
+ -- Create an index on the materialized view
238
+ CREATE UNIQUE INDEX idx_leaderboard_user ON leaderboard(id);
239
+
240
+ -- Refresh on a schedule (e.g., via pg_cron or a cron Edge Function)
241
+ REFRESH MATERIALIZED VIEW CONCURRENTLY leaderboard;
242
+ ```
243
+
244
+ **Configure connection pooling with Supavisor:**
171
245
 
172
- ### Step 1: Establish Baseline
173
- Measure current latency for critical Supabase operations.
246
+ ```typescript
247
+ // For serverless environments (Vercel, Netlify, Cloudflare Workers):
248
+ // Use the pooled connection string with transaction mode
249
+ // Dashboard → Settings → Database → Connection string → "Transaction mode"
250
+
251
+ // The JS SDK uses PostgREST (HTTP) which has its own pooling — no config needed.
252
+ // Direct Postgres clients (Prisma, Drizzle, pg) need the pooled string:
253
+ import { Pool } from 'pg'
254
+
255
+ const pool = new Pool({
256
+ connectionString: 'postgres://postgres.[ref]:[pwd]@aws-0-[region].pooler.supabase.com:6543/postgres',
257
+ max: 5, // Keep low in serverless — Supavisor manages the upstream pool
258
+ idleTimeoutMillis: 10000,
259
+ })
260
+ ```
174
261
 
175
- ### Step 2: Implement Caching
176
- Add response caching for frequently accessed data.
262
+ **Use Edge Functions for compute-heavy operations close to data:**
177
263
 
178
- ### Step 3: Enable Batching
179
- Use DataLoader or similar for automatic request batching.
264
+ ```typescript
265
+ // supabase/functions/generate-report/index.ts
266
+ // Edge Functions run in the same region as your database — low latency
267
+ import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
268
+
269
+ Deno.serve(async (req) => {
270
+ const supabase = createClient(
271
+ Deno.env.get('SUPABASE_URL')!,
272
+ Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
273
+ )
274
+
275
+ // Heavy aggregation runs next to the database, not in the user's browser
276
+ const { data } = await supabase.rpc('get_dashboard_stats', {
277
+ org_id: (await req.json()).org_id
278
+ })
279
+
280
+ return new Response(JSON.stringify(data), {
281
+ headers: { 'Content-Type': 'application/json' }
282
+ })
283
+ })
284
+ ```
180
285
 
181
- ### Step 4: Optimize Connections
182
- Configure connection pooling with keep-alive.
286
+ **Enable read replicas on Pro+ plans** for read-heavy workloads — route analytics and reporting queries to the replica to offload the primary.
183
287
 
184
288
  ## Output
185
- - Reduced API latency
186
- - Caching layer implemented
187
- - Request batching enabled
188
- - Connection pooling configured
289
+
290
+ After completing these steps, you will have:
291
+
292
+ - Diagnostic baseline from `pg_stat_statements`, `index-usage`, and `cache-hit`
293
+ - Targeted indexes on slow query columns, foreign keys, and common filter patterns
294
+ - Query plans verified with `EXPLAIN ANALYZE` showing Index Scan instead of Seq Scan
295
+ - Client queries optimized with column selection, pagination, and joined queries
296
+ - RPC functions and materialized views for expensive server-side aggregations
297
+ - Connection pooling configured via Supavisor for serverless deployments
298
+ - Edge Functions deployed for compute-heavy operations near the database
189
299
 
190
300
  ## 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 |
301
+
302
+ | Symptom | Cause | Fix |
303
+ |---------|-------|-----|
304
+ | `Seq Scan` in EXPLAIN output on large table | Missing index on filtered/sorted column | `CREATE INDEX` on the column(s) in the WHERE/ORDER BY clause |
305
+ | `PGRST000: could not connect to server` | Connection pool exhausted | Switch to Supavisor pooled connection string; reduce `max` pool size in serverless |
306
+ | Slow RLS policies (visible in `pg_stat_statements`) | Subquery in policy evaluates per row | Refactor to `security definer` function or use `EXISTS` instead of `IN` |
307
+ | Response payloads > 1MB | `select('*')` returning all columns/rows | Use `.select('col1, col2')` and `.range()` for pagination |
308
+ | Stale materialized view data | View not refreshed after writes | Set up `pg_cron` or a cron Edge Function to run `REFRESH MATERIALIZED VIEW CONCURRENTLY` |
309
+ | `cache-hit` ratio below 99% | Working set exceeds RAM (shared_buffers) | Upgrade compute add-on or optimize queries to access fewer pages |
310
+ | High latency on aggregation endpoints | Aggregation computed live on every request | Move to materialized view or RPC function; cache at the Edge Function layer |
197
311
 
198
312
  ## Examples
199
313
 
200
- ### Quick Performance Wrapper
314
+ **Before/after index optimization:**
315
+
316
+ ```sql
317
+ -- Before: 450ms, Seq Scan
318
+ EXPLAIN (ANALYZE) SELECT * FROM orders WHERE customer_id = 'abc-123';
319
+ -- Seq Scan on orders (cost=0.00..15234.00 rows=50 width=128) (actual time=0.015..450.123 rows=50 loops=1)
320
+
321
+ CREATE INDEX idx_orders_customer ON orders(customer_id);
322
+
323
+ -- After: 0.8ms, Index Scan
324
+ EXPLAIN (ANALYZE) SELECT * FROM orders WHERE customer_id = 'abc-123';
325
+ -- Index Scan using idx_orders_customer on orders (cost=0.42..8.44 rows=50 width=128) (actual time=0.025..0.812 rows=50 loops=1)
326
+ ```
327
+
328
+ **Client query optimization — eliminating N+1:**
329
+
201
330
  ```typescript
202
- const withPerformance = <T>(name: string, fn: () => Promise<T>) =>
203
- measuredSupabaseCall(name, () =>
204
- cachedSupabaseRequest(`cache:${name}`, fn)
205
- );
331
+ // BAD: N+1 one query per project (10 projects = 11 queries)
332
+ const { data: projects } = await supabase.from('projects').select('id, name')
333
+ for (const project of projects!) {
334
+ const { data: tasks } = await supabase
335
+ .from('tasks').select('*').eq('project_id', project.id)
336
+ }
337
+
338
+ // GOOD: Single query with embedded join (1 query total)
339
+ const { data } = await supabase
340
+ .from('projects')
341
+ .select('id, name, tasks(id, title, status)')
342
+ .eq('organization_id', orgId)
206
343
  ```
207
344
 
208
345
  ## 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)
346
+
347
+ - [Supabase Performance Advisor](https://supabase.com/docs/guides/database/inspect) — built-in CLI diagnostics
348
+ - [PostgreSQL Index Types](https://supabase.com/docs/guides/database/postgres/indexes) — B-tree, GIN, GiST, and when to use each
349
+ - [Connection Pooling with Supavisor](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler) — transaction vs session mode
350
+ - [Supabase Edge Functions](https://supabase.com/docs/guides/functions) — deploy serverless functions next to your database
351
+ - [Read Replicas](https://supabase.com/docs/guides/platform/read-replicas) — offload read-heavy queries on Pro+ plans
352
+ - [RLS Performance Best Practices](https://supabase.com/docs/guides/troubleshooting/rls-performance-and-best-practices-Z5Jjwv) — avoid per-row subqueries
212
353
 
213
354
  ## Next Steps
214
- For cost optimization, see `supabase-cost-tuning`.
355
+
356
+ - For RLS policy design, see `supabase-rls-policies`
357
+ - For cost optimization, see `supabase-cost-tuning`
358
+ - For real-time subscriptions, see `supabase-realtime`
@@ -0,0 +1,49 @@
1
+ # Caching Strategy
2
+
3
+ ## Caching Strategy
4
+
5
+ ### Response Caching
6
+
7
+ ```typescript
8
+ import { LRUCache } from 'lru-cache';
9
+
10
+ const cache = new LRUCache<string, any>({
11
+ max: 1000,
12
+ ttl: 30000, // 1 minute
13
+ updateAgeOnGet: true,
14
+ });
15
+
16
+ async function cachedSupabaseRequest<T>(
17
+ key: string,
18
+ fetcher: () => Promise<T>,
19
+ ttl?: number
20
+ ): Promise<T> {
21
+ const cached = cache.get(key);
22
+ if (cached) return cached as T;
23
+
24
+ const result = await fetcher();
25
+ cache.set(key, result, { ttl });
26
+ return result;
27
+ }
28
+ ```
29
+
30
+ ### Redis Caching (Distributed)
31
+
32
+ ```typescript
33
+ import Redis from 'ioredis';
34
+
35
+ const redis = new Redis(process.env.REDIS_URL);
36
+
37
+ async function cachedWithRedis<T>(
38
+ key: string,
39
+ fetcher: () => Promise<T>,
40
+ ttlSeconds = 60
41
+ ): Promise<T> {
42
+ const cached = await redis.get(key);
43
+ if (cached) return JSON.parse(cached);
44
+
45
+ const result = await fetcher();
46
+ await redis.setex(key, ttlSeconds, JSON.stringify(result));
47
+ return result;
48
+ }
49
+ ```
@@ -0,0 +1,11 @@
1
+ # Error Handling Reference
2
+
3
+ | Issue | Cause | Solution |
4
+ |-------|-------|----------|
5
+ | Cache miss storm | TTL expired | Use stale-while-revalidate |
6
+ | Batch timeout | Too many items | Reduce batch size |
7
+ | Connection exhausted | No pooling | Configure max sockets |
8
+ | Memory pressure | Cache too large | Set max cache entries |
9
+
10
+ ---
11
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
@@ -0,0 +1,13 @@
1
+ ## Examples
2
+
3
+ ### Quick Performance Wrapper
4
+
5
+ ```typescript
6
+ const withPerformance = <T>(name: string, fn: () => Promise<T>) =>
7
+ measuredSupabaseCall(name, () =>
8
+ cachedSupabaseRequest(`cache:${name}`, fn)
9
+ );
10
+ ```
11
+
12
+ ---
13
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*