Claude-code-plugins-plus-skills vercel-known-pitfalls
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/vercel-pack/skills/vercel-known-pitfalls" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-vercel-known-pitfalls && rm -rf "$T"
plugins/saas-packs/vercel-pack/skills/vercel-known-pitfalls/SKILL.mdVercel Known Pitfalls
Overview
Catalog of the most common Vercel anti-patterns with severity ratings, detection methods, and fixes. Organized by category: secret exposure, serverless function mistakes, edge runtime violations, configuration errors, and cost traps.
Prerequisites
- Access to Vercel codebase for review
- Understanding of Vercel's deployment model
- Familiarity with
for error codesvercel-common-errors
Instructions
Category 1: Secret Exposure (Critical)
P1: Secrets in NEXT_PUBLIC_ variables
// BAD — exposed in client JavaScript bundle, visible to anyone const apiKey = process.env.NEXT_PUBLIC_API_SECRET; // This value is inlined at build time into the browser bundle // GOOD — server-only access const apiKey = process.env.API_SECRET; // Only accessible in serverless functions and server components
- Detection:
grep -r 'NEXT_PUBLIC_.*SECRET\|NEXT_PUBLIC_.*KEY\|NEXT_PUBLIC_.*TOKEN' src/ - Fix: Remove
prefix, rotate the exposed secret immediatelyNEXT_PUBLIC_
P2: Hardcoded credentials in source
// BAD const client = new Client({ apiKey: 'sk_live_abc123' }); // GOOD const client = new Client({ apiKey: process.env.API_KEY });
- Detection:
grep -rE 'sk_live|sk_test|Bearer [a-zA-Z0-9]{20,}' src/ api/ - Fix: Move to environment variables, add pre-commit hook
P3: Secrets in vercel.json
// BAD — vercel.json is committed to git { "env": { "API_KEY": "sk_live_abc123" } } // GOOD — use Vercel dashboard or CLI // vercel env add API_KEY production
Category 2: Serverless Function Mistakes (High)
P4: Heavy initialization at module level
// BAD — runs on every cold start, adds 500ms+ import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); // Connects on import const cache = await loadLargeDataset(); // Blocks cold start // GOOD — lazy initialization let prisma: PrismaClient | null = null; function getDb() { if (!prisma) prisma = new PrismaClient(); return prisma; } export default async function handler(req, res) { const db = getDb(); // Only connects on first request // ... }
P5: Not returning responses from all code paths
// BAD — some paths don't return, causing NO_RESPONSE_FROM_FUNCTION export default function handler(req, res) { if (req.method === 'GET') { res.json({ data: 'ok' }); } // POST, PUT, DELETE — no response returned! } // GOOD export default function handler(req, res) { if (req.method === 'GET') { return res.json({ data: 'ok' }); } return res.status(405).json({ error: 'Method not allowed' }); }
P6: Ignoring function timeout limits
// BAD — no timeout awareness, function silently killed export default async function handler(req, res) { const results = await processMillionRecords(); // Takes 5 minutes res.json(results); } // GOOD — chunk work, respect timeout export default async function handler(req, res) { const batch = req.query.batch ?? 0; const results = await processBatch(batch, 100); // Process 100 at a time res.json({ results, nextBatch: batch + 1, done: results.length < 100, }); }
P7: Connection pool exhaustion
// BAD — each function instance creates its own connection pool // With 100 concurrent functions × 10 pool connections = 1000 DB connections const pool = new Pool({ max: 10 }); // GOOD — use a connection pooler // Use Prisma Accelerate, PgBouncer, or Supabase connection pooler // Configure pool size to 1-2 per function instance const pool = new Pool({ max: 2 });
Category 3: Edge Runtime Violations (High)
P8: Node.js APIs in edge functions
// BAD — these crash silently in Edge Runtime export const config = { runtime: 'edge' }; import fs from 'fs'; // Not available import path from 'path'; // Not available import crypto from 'crypto'; // Use crypto.subtle instead import { Buffer } from 'buffer'; // Use Uint8Array instead // GOOD — Web Standard APIs const hash = await crypto.subtle.digest('SHA-256', data); const encoded = btoa(String.fromCharCode(...new Uint8Array(hash)));
- Detection:
grep -rn "from 'fs'\|from 'path'\|from 'crypto'\|from 'child_process'" --include="*edge*" --include="*middleware*"
P9: Dynamic code evaluation in edge
// BAD — throws "Dynamic Code Evaluation not allowed" export const config = { runtime: 'edge' }; const fn = new Function('return 42'); // Not allowed eval('console.log("hi")'); // Not allowed // GOOD — use static code only const fn = () => 42;
Category 4: Configuration Errors (Medium)
P10: Missing environment variable scoping
# BAD — variable only in Production, preview deployments break vercel env add DATABASE_URL production # GOOD — add to all environments that need it vercel env add DATABASE_URL production preview development
P11: Using deprecated builds property
// BAD (deprecated) { "builds": [ { "src": "api/**/*.ts", "use": "@vercel/node" } ] } // GOOD (current) { "functions": { "api/**/*.ts": { "runtime": "nodejs20.x", "maxDuration": 30 } } }
P12: Middleware running on static assets
// BAD — middleware runs on every request including static files export function middleware(request) { /* auth check */ } // GOOD — exclude static assets export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'], };
Category 5: Cost Traps (Medium)
P13: Uncached high-traffic endpoints
// BAD — every request invokes a function export default function handler(req, res) { res.json({ config: getConfig() }); // No cache headers } // GOOD — cache at the edge, save function invocations export default function handler(req, res) { res.setHeader('Cache-Control', 's-maxage=3600, stale-while-revalidate=86400'); res.json({ config: getConfig() }); }
P14: Over-allocated function memory
// BAD — 3GB for a simple JSON response { "functions": { "api/config.ts": { "memory": 3008 } } } // GOOD — right-size per endpoint { "functions": { "api/config.ts": { "memory": 128 }, "api/image-process.ts": { "memory": 1024 } } }
P15: Middleware doing heavy work
// BAD — database query on every request export async function middleware(request) { const user = await db.user.findUnique({ where: { id: token.sub } }); // Runs on EVERY matched request, expensive at scale } // GOOD — validate JWT locally, no DB call export function middleware(request) { const token = request.cookies.get('session')?.value; // Verify JWT signature locally (cheap, no external call) }
Quick Audit Script
#!/usr/bin/env bash echo "=== Vercel Pitfall Audit ===" echo "P1: Secrets in NEXT_PUBLIC_:" grep -rn 'NEXT_PUBLIC_.*SECRET\|NEXT_PUBLIC_.*KEY\|NEXT_PUBLIC_.*TOKEN' src/ api/ 2>/dev/null || echo " PASS" echo "P2: Hardcoded credentials:" grep -rnE 'sk_live|sk_test|Bearer [a-zA-Z0-9]{20,}' src/ api/ 2>/dev/null || echo " PASS" echo "P8: Node.js APIs in edge files:" grep -rn "from 'fs'\|from 'path'\|from 'child_process'" src/middleware.ts api/*edge* 2>/dev/null || echo " PASS" echo "P11: Deprecated builds:" jq -e '.builds' vercel.json 2>/dev/null && echo " FAIL: deprecated builds" || echo " PASS" echo "P12: Middleware without matcher:" grep -L 'matcher' src/middleware.ts 2>/dev/null && echo " WARN: no matcher configured" || echo " PASS"
Output
- Anti-patterns identified and classified by severity (Critical/High/Medium)
- Security issues fixed and exposed secrets rotated
- Performance improvements from lazy initialization and caching
- ESLint and CI prevention measures blocking future regressions
Error Handling
| Pitfall | Severity | Detection | Fix |
|---|---|---|---|
| P1: NEXT_PUBLIC_ secrets | Critical | grep scan | Remove prefix, rotate secret |
| P4: Heavy cold starts | High | Cold start timing | Lazy initialization |
| P5: Missing response | High | 502 errors in logs | Return from all paths |
| P7: Connection exhaustion | High | DB connection errors | Use connection pooler |
| P8: Node.js in edge | High | EDGE_FUNCTION_INVOCATION_FAILED | Use Web APIs |
| P13: No cache headers | Medium | High function invocations bill | Add s-maxage |
Resources
Next Steps
Return to
vercel-install-auth for setup or vercel-reference-architecture for project structure.