@intentsolutionsio/vercel-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/vercel-advanced-troubleshooting/SKILL.md +261 -0
- package/skills/vercel-architecture-variants/SKILL.md +284 -0
- package/skills/vercel-ci-integration/SKILL.md +124 -0
- package/skills/vercel-common-errors/SKILL.md +109 -0
- package/skills/vercel-cost-tuning/SKILL.md +201 -0
- package/skills/vercel-data-handling/SKILL.md +220 -0
- package/skills/vercel-debug-bundle/SKILL.md +111 -0
- package/skills/vercel-deploy-integration/SKILL.md +209 -0
- package/skills/vercel-deploy-preview/SKILL.md +71 -0
- package/skills/vercel-edge-functions/SKILL.md +73 -0
- package/skills/vercel-enterprise-rbac/SKILL.md +222 -0
- package/skills/vercel-hello-world/SKILL.md +96 -0
- package/skills/vercel-incident-runbook/SKILL.md +203 -0
- package/skills/vercel-install-auth/SKILL.md +90 -0
- package/skills/vercel-known-pitfalls/SKILL.md +334 -0
- package/skills/vercel-load-scale/SKILL.md +274 -0
- package/skills/vercel-local-dev-loop/SKILL.md +117 -0
- package/skills/vercel-migration-deep-dive/SKILL.md +244 -0
- package/skills/vercel-multi-env-setup/SKILL.md +222 -0
- package/skills/vercel-observability/SKILL.md +250 -0
- package/skills/vercel-performance-tuning/SKILL.md +214 -0
- package/skills/vercel-policy-guardrails/SKILL.md +257 -0
- package/skills/vercel-prod-checklist/SKILL.md +119 -0
- package/skills/vercel-rate-limits/SKILL.md +149 -0
- package/skills/vercel-reference-architecture/SKILL.md +238 -0
- package/skills/vercel-reliability-patterns/SKILL.md +290 -0
- package/skills/vercel-sdk-patterns/SKILL.md +147 -0
- package/skills/vercel-security-basics/SKILL.md +140 -0
- package/skills/vercel-upgrade-migration/SKILL.md +112 -0
- package/skills/vercel-webhooks-events/SKILL.md +199 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vercel-multi-env-setup
|
|
3
|
+
description: |
|
|
4
|
+
Configure Vercel across development, staging, and production environments.
|
|
5
|
+
Use when setting up multi-environment deployments, configuring per-environment secrets,
|
|
6
|
+
or implementing environment-specific Vercel configurations.
|
|
7
|
+
Trigger with phrases like "vercel environments", "vercel staging",
|
|
8
|
+
"vercel dev prod", "vercel environment setup", "vercel 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
|
+
# Vercel Multi-Environment Setup
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Configure Vercel across development, staging, and production environments.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Separate Vercel 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
|
+
├── vercel/
|
|
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": "${VERCEL_API_KEY}",
|
|
61
|
+
"baseUrl": "https://api-sandbox.vercel.com",
|
|
62
|
+
"debug": true,
|
|
63
|
+
"cache": {
|
|
64
|
+
"enabled": false
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### staging.json
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"apiKey": "${VERCEL_API_KEY_STAGING}",
|
|
73
|
+
"baseUrl": "https://api-staging.vercel.com",
|
|
74
|
+
"debug": false
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### production.json
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"apiKey": "${VERCEL_API_KEY_PROD}",
|
|
82
|
+
"baseUrl": "https://api.vercel.com",
|
|
83
|
+
"debug": false,
|
|
84
|
+
"retries": 5
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Environment Detection
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// src/vercel/config.ts
|
|
92
|
+
import baseConfig from '../../config/vercel/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 getVercelConfig() {
|
|
105
|
+
const env = detectEnvironment();
|
|
106
|
+
const envConfig = require(`../../config/vercel/${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
|
+
VERCEL_API_KEY=sk_test_dev_***
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### CI/CD (GitHub Actions)
|
|
125
|
+
```yaml
|
|
126
|
+
env:
|
|
127
|
+
VERCEL_API_KEY: ${{ secrets.VERCEL_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 vercel/production/api-key
|
|
134
|
+
|
|
135
|
+
# GCP Secret Manager
|
|
136
|
+
gcloud secrets versions access latest --secret=vercel-api-key
|
|
137
|
+
|
|
138
|
+
# HashiCorp Vault
|
|
139
|
+
vault kv get -field=api_key secret/vercel/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 = getVercelConfig();
|
|
148
|
+
|
|
149
|
+
if (config.environment !== 'production') {
|
|
150
|
+
console.warn(`[vercel] ${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 = getVercelConfig();
|
|
214
|
+
console.log(`Running in ${env.environment} with ${env.baseUrl}`);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Resources
|
|
218
|
+
- [Vercel Environments Guide](https://vercel.com/docs/environments)
|
|
219
|
+
- [12-Factor App Config](https://12factor.net/config)
|
|
220
|
+
|
|
221
|
+
## Next Steps
|
|
222
|
+
For observability setup, see `vercel-observability`.
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vercel-observability
|
|
3
|
+
description: |
|
|
4
|
+
Set up comprehensive observability for Vercel integrations with metrics, traces, and alerts.
|
|
5
|
+
Use when implementing monitoring for Vercel operations, setting up dashboards,
|
|
6
|
+
or configuring alerting for Vercel integration health.
|
|
7
|
+
Trigger with phrases like "vercel monitoring", "vercel metrics",
|
|
8
|
+
"vercel observability", "monitor vercel", "vercel alerts", "vercel 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
|
+
# Vercel Observability
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Set up comprehensive observability for Vercel 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
|
+
| `vercel_requests_total` | Counter | Total API requests |
|
|
32
|
+
| `vercel_request_duration_seconds` | Histogram | Request latency |
|
|
33
|
+
| `vercel_errors_total` | Counter | Error count by type |
|
|
34
|
+
| `vercel_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: 'vercel_requests_total',
|
|
45
|
+
help: 'Total Vercel API requests',
|
|
46
|
+
labelNames: ['method', 'status'],
|
|
47
|
+
registers: [registry],
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const requestDuration = new Histogram({
|
|
51
|
+
name: 'vercel_request_duration_seconds',
|
|
52
|
+
help: 'Vercel 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: 'vercel_errors_total',
|
|
60
|
+
help: 'Vercel 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('vercel-client');
|
|
97
|
+
|
|
98
|
+
async function tracedVercelCall<T>(
|
|
99
|
+
operationName: string,
|
|
100
|
+
operation: () => Promise<T>
|
|
101
|
+
): Promise<T> {
|
|
102
|
+
return tracer.startActiveSpan(`vercel.${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: 'vercel',
|
|
127
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
function logVercelOperation(
|
|
131
|
+
operation: string,
|
|
132
|
+
data: Record<string, any>,
|
|
133
|
+
duration: number
|
|
134
|
+
) {
|
|
135
|
+
logger.info({
|
|
136
|
+
service: 'vercel',
|
|
137
|
+
operation,
|
|
138
|
+
duration_ms: duration,
|
|
139
|
+
...data,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Alert Configuration
|
|
145
|
+
|
|
146
|
+
### Prometheus AlertManager Rules
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
# vercel_alerts.yaml
|
|
150
|
+
groups:
|
|
151
|
+
- name: vercel_alerts
|
|
152
|
+
rules:
|
|
153
|
+
- alert: VercelHighErrorRate
|
|
154
|
+
expr: |
|
|
155
|
+
rate(vercel_errors_total[5m]) /
|
|
156
|
+
rate(vercel_requests_total[5m]) > 0.05
|
|
157
|
+
for: 5m
|
|
158
|
+
labels:
|
|
159
|
+
severity: warning
|
|
160
|
+
annotations:
|
|
161
|
+
summary: "Vercel error rate > 5%"
|
|
162
|
+
|
|
163
|
+
- alert: VercelHighLatency
|
|
164
|
+
expr: |
|
|
165
|
+
histogram_quantile(0.95,
|
|
166
|
+
rate(vercel_request_duration_seconds_bucket[5m])
|
|
167
|
+
) > 2
|
|
168
|
+
for: 5m
|
|
169
|
+
labels:
|
|
170
|
+
severity: warning
|
|
171
|
+
annotations:
|
|
172
|
+
summary: "Vercel P95 latency > 2s"
|
|
173
|
+
|
|
174
|
+
- alert: VercelDown
|
|
175
|
+
expr: up{job="vercel"} == 0
|
|
176
|
+
for: 1m
|
|
177
|
+
labels:
|
|
178
|
+
severity: critical
|
|
179
|
+
annotations:
|
|
180
|
+
summary: "Vercel integration is down"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Dashboard
|
|
184
|
+
|
|
185
|
+
### Grafana Panel Queries
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"panels": [
|
|
190
|
+
{
|
|
191
|
+
"title": "Vercel Request Rate",
|
|
192
|
+
"targets": [{
|
|
193
|
+
"expr": "rate(vercel_requests_total[5m])"
|
|
194
|
+
}]
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"title": "Vercel Latency P50/P95/P99",
|
|
198
|
+
"targets": [{
|
|
199
|
+
"expr": "histogram_quantile(0.5, rate(vercel_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
|
+
- [Vercel Observability Guide](https://vercel.com/docs/observability)
|
|
248
|
+
|
|
249
|
+
## Next Steps
|
|
250
|
+
For incident response, see `vercel-incident-runbook`.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vercel-performance-tuning
|
|
3
|
+
description: |
|
|
4
|
+
Optimize Vercel API performance with caching, batching, and connection pooling.
|
|
5
|
+
Use when experiencing slow API responses, implementing caching strategies,
|
|
6
|
+
or optimizing request throughput for Vercel integrations.
|
|
7
|
+
Trigger with phrases like "vercel performance", "optimize vercel",
|
|
8
|
+
"vercel latency", "vercel caching", "vercel slow", "vercel 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
|
+
# Vercel Performance Tuning
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Optimize Vercel API performance with caching, batching, and connection pooling.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Vercel 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
|
+
| Cold Start (Serverless) | 250ms | 500ms | 1000ms |
|
|
31
|
+
| Cold Start (Edge) | 5ms | 25ms | 50ms |
|
|
32
|
+
| Build Time | 30s | 120s | 300s |
|
|
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: 31536000000, // 1 minute
|
|
43
|
+
updateAgeOnGet: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
async function cachedVercelRequest<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 vercelLoader = new DataLoader<string, any>(
|
|
86
|
+
async (ids) => {
|
|
87
|
+
// Batch fetch from Vercel
|
|
88
|
+
const results = await vercelClient.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
|
+
vercelLoader.load('id-1'),
|
|
100
|
+
vercelLoader.load('id-2'),
|
|
101
|
+
vercelLoader.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: None,
|
|
114
|
+
maxFreeSockets: 5,
|
|
115
|
+
timeout: 10000,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const client = new VercelClient({
|
|
119
|
+
apiKey: process.env.VERCEL_API_KEY!,
|
|
120
|
+
httpAgent: agent,
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Pagination Optimization
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
async function* paginatedVercelList<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 paginatedVercelList(cursor =>
|
|
143
|
+
vercelClient.list({ cursor, limit: 100 })
|
|
144
|
+
)) {
|
|
145
|
+
await process(item);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Performance Monitoring
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
async function measuredVercelCall<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 Vercel 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
|
+
measuredVercelCall(name, () =>
|
|
204
|
+
cachedVercelRequest(`cache:${name}`, fn)
|
|
205
|
+
);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Resources
|
|
209
|
+
- [Vercel Performance Guide](https://vercel.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 `vercel-cost-tuning`.
|