vercel-reference-architecture
Standardized project directory structure and architectural patterns for Vercel and Next.js applications.
Install
mkdir -p .claude/skills/vercel-reference-architecture && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/7443" && unzip -o skill.zip -d .claude/skills/vercel-reference-architecture && rm skill.zipInstalls to .claude/skills/vercel-reference-architecture
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Implement a Vercel reference architecture with layered project structureKey capabilities
- →Organize source code, API routes, and static assets
- →Implement middleware patterns for authentication and redirects
- →Manage typed environment variables
- →Initialize database clients to minimize cold starts
- →Configure Vercel project settings
How it works
The skill establishes a directory structure, configures environment variables, sets up a database client, defines API route patterns, and implements edge middleware and Vercel configuration.
Inputs & outputs
When to use vercel-reference-architecture
- →Design new Vercel-based projects
- →Standardize directory structures across teams
- →Review existing architecture for improvements
- →Implement modular API route organization
About this skill
Vercel Reference Architecture
Overview
Implement a production-ready Vercel project architecture with clear separation across edge, server, and client layers. Covers directory structure, middleware patterns, API route organization, shared utilities, and configuration management.
Prerequisites
- Understanding of Vercel's deployment model (edge, serverless, static)
- TypeScript project setup
- Next.js 14+ (recommended) or other Vercel-supported framework
Instructions
Step 1: Directory Structure
my-vercel-app/
├── public/ # Static assets (served from CDN)
│ ├── favicon.ico
│ └── images/
├── src/
│ ├── app/ # Next.js App Router pages
│ │ ├── layout.tsx # Root layout
│ │ ├── page.tsx # Home page
│ │ ├── api/ # API routes (serverless functions)
│ │ │ ├── health/route.ts
│ │ │ ├── users/route.ts
│ │ │ └── webhooks/
│ │ │ └── vercel/route.ts
│ │ ├── dashboard/ # Protected pages
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ └── (marketing)/ # Public pages (route group)
│ │ ├── pricing/page.tsx
│ │ └── about/page.tsx
│ ├── lib/ # Shared utilities (server + client)
│ │ ├── api-client.ts # External API wrapper
│ │ ├── db.ts # Database client (lazy singleton)
│ │ ├── env.ts # Typed environment variables
│ │ └── errors.ts # Error classes
│ ├── components/ # React components
│ │ ├── ui/ # Design system primitives
│ │ └── features/ # Feature-specific components
│ └── middleware.ts # Edge Middleware (auth, redirects)
├── vercel.json # Vercel configuration
├── next.config.js # Next.js configuration
├── tsconfig.json
├── package.json
└── .env.example # Required env vars (no values)
Step 2: Typed Environment Variables
// src/lib/env.ts — validate env vars at import time
import { z } from 'zod';
const envSchema = z.object({
DATABASE_URL: z.string().url(),
API_SECRET: z.string().min(16),
NEXT_PUBLIC_API_URL: z.string().url(),
VERCEL_ENV: z.enum(['production', 'preview', 'development']).default('development'),
VERCEL_URL: z.string().optional(),
});
// Fails fast at startup if env vars are missing
export const env = envSchema.parse(process.env);
// Type-safe access throughout the app
// Usage: import { env } from '@/lib/env'; env.DATABASE_URL
Step 3: Database Client (Lazy Singleton)
// src/lib/db.ts — lazy init to minimize cold starts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined };
export const db = globalForPrisma.prisma ?? new PrismaClient({
log: process.env.VERCEL_ENV === 'development' ? ['query'] : ['error'],
});
// Prevent multiple instances in development (hot reload)
if (process.env.VERCEL_ENV !== 'production') {
globalForPrisma.prisma = db;
}
Step 4: API Route Pattern
// src/app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { db } from '@/lib/db';
import { env } from '@/lib/env';
export async function GET(request: NextRequest) {
try {
const searchParams = request.nextUrl.searchParams;
const limit = Number(searchParams.get('limit') ?? 20);
const users = await db.user.findMany({ take: limit });
return NextResponse.json({ users }, {
headers: { 'Cache-Control': 's-maxage=60, stale-while-revalidate=300' },
});
} catch (error) {
console.error('GET /api/users failed:', error);
return NextResponse.json(
{ error: 'Internal server error', requestId: crypto.randomUUID() },
{ status: 500 }
);
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const user = await db.user.create({ data: body });
return NextResponse.json({ user }, { status: 201 });
} catch (error) {
console.error('POST /api/users failed:', error);
return NextResponse.json(
{ error: 'Failed to create user' },
{ status: 400 }
);
}
}
Step 5: Edge Middleware for Auth
// src/middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Skip auth for public routes
if (pathname.startsWith('/api/health') || pathname.startsWith('/api/webhooks')) {
return NextResponse.next();
}
// Check auth for dashboard routes
if (pathname.startsWith('/dashboard') || pathname.startsWith('/api/')) {
const token = request.cookies.get('session')?.value;
if (!token) {
if (pathname.startsWith('/api/')) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
return NextResponse.redirect(new URL('/login', request.url));
}
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
Step 6: Health Check Endpoint
// src/app/api/health/route.ts
import { db } from '@/lib/db';
export const dynamic = 'force-dynamic'; // Never cache health checks
export async function GET() {
const checks: Record<string, 'ok' | 'error'> = {};
// Database connectivity
try {
await db.$queryRaw`SELECT 1`;
checks.database = 'ok';
} catch {
checks.database = 'error';
}
const allHealthy = Object.values(checks).every(v => v === 'ok');
return Response.json({
status: allHealthy ? 'healthy' : 'degraded',
checks,
version: process.env.VERCEL_GIT_COMMIT_SHA?.slice(0, 7) ?? 'local',
region: process.env.VERCEL_REGION ?? 'local',
timestamp: new Date().toISOString(),
}, {
status: allHealthy ? 200 : 503,
});
}
Step 7: Vercel Configuration
// vercel.json
{
"regions": ["iad1"],
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" }
]
}
],
"rewrites": [
{ "source": "/docs/:path*", "destination": "https://docs.example.com/:path*" }
],
"redirects": [
{ "source": "/old-page", "destination": "/new-page", "permanent": true }
]
}
Layer Responsibilities
| Layer | Runtime | Responsibilities |
|---|---|---|
| Edge (middleware.ts) | V8 isolates | Auth, redirects, A/B testing, headers |
| Server (api routes) | Node.js | Database queries, business logic, webhooks |
| Static (pages) | CDN | Pre-rendered pages, ISR, images |
| Client (components) | Browser | Interactivity, client state |
Output
- Layered project structure with clear separation of concerns
- Typed environment variables validated at startup
- Lazy-initialized database client minimizing cold starts
- Edge Middleware handling authentication before server layer
- Health check endpoint for deployment verification
Error Handling
| Error | Cause | Solution |
|---|---|---|
| Env validation fails on deploy | Missing required variable | Add to Vercel dashboard for target environment |
| Middleware runs on static assets | Matcher too broad | Add exclusions for _next/static, _next/image |
| Database connection pool exhausted | Too many concurrent functions | Use connection pooler (PgBouncer, Prisma Accelerate) |
| API route not found | Wrong directory structure | Must be in src/app/api/ with route.ts filename |
Resources
- Next.js Project Structure
- Vercel Project Configuration
- Middleware Documentation
- API Routes (App Router)
Next Steps
For multi-environment setup, see vercel-multi-env-setup.
Prerequisites
Limitations
- →Middleware running on static assets if the matcher is too broad
- →Database connection pool exhaustion with too many concurrent functions
- →API route not found if not in `src/app/api/` with `route.ts` filename
How it compares
This skill provides a structured, layered project layout and configuration for Vercel applications, unlike manual setup which may lack consistent organization and best practices.
Compared to similar skills
vercel-reference-architecture side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| vercel-reference-architecture (this skill) | 3 | 27d | Review | Intermediate |
| beforemerge-fullstack-architecture-review | 0 | 4mo | No flags | Advanced |
| reviewing-nextjs-16-patterns | 11 | 8mo | Review | Intermediate |
| nextjs-best-practices | 31 | 6mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by jeremylongshore
View all by jeremylongshore →You might also like
beforemerge-fullstack-architecture-review
adrian-coronel
Code review rules for DRY/SOLID layered architecture in fullstack TypeScript applications. Covers dependency direction, service/repository patterns, factory injection, domain entities, security hardening, performance optimization, and code quality patterns. Use this skill when reviewing, writing, or
reviewing-nextjs-16-patterns
djankies
Review code for Next.js 16 compliance - security patterns, caching, breaking changes. Use when reviewing Next.js code, preparing for migration, or auditing for violations.
nextjs-best-practices
davila7
Next.js App Router principles. Server Components, data fetching, routing patterns.
frontend-code-review
langgenius
Trigger when the user requests a review of frontend files (e.g., `.tsx`, `.ts`, `.js`). Support both pending-change reviews and focused file reviews while applying the checklist rules.
add-setting-env
lobehub
Guide for adding environment variables to configure user settings. Use when implementing server-side environment variables that control default values for user settings. Triggers on env var configuration or setting default value tasks.
project-overview
lobehub
Complete project architecture and structure guide. Use when exploring the codebase, understanding project organization, finding files, or needing comprehensive architectural context. Triggers on architecture questions, directory navigation, or project overview needs.