Vercel Edge Function
Commands nextjs-vercel 255
npx claude-code-templates@latest --command nextjs-vercel/vercel-edge-function Content
Vercel Edge Function Generator
Function Name: $ARGUMENTS
Current Project Analysis
Project Structure
- Vercel config: @vercel.json (if exists)
- Next.js config: @next.config.js
- API routes: @app/api/ or @pages/api/
- Middleware: @middleware.ts (if exists)
Framework Detection
- Package.json: @package.json
- TypeScript config: @tsconfig.json (if exists)
- Environment variables: @.env.local (if exists)
Edge Function Implementation Strategy
1. File Structure Creation
Generate comprehensive edge function structure:
api/edge/[function-name]/
├── index.ts # Main edge function
├── types.ts # TypeScript types
├── utils.ts # Utility functions
├── config.ts # Configuration
└── __tests__/
└── [function-name].test.ts # Unit tests2. Base Edge Function Template
typescript
// api/edge/[function-name]/index.ts
import { NextRequest, NextResponse } from 'next/server';
export const runtime = 'edge';
export async function GET(request: NextRequest) {
try {
// Get geolocation data
const country = request.geo?.country || 'Unknown';
const city = request.geo?.city || 'Unknown';
const region = request.geo?.region || 'Unknown';
// Get request metadata
const ip = request.headers.get('x-forwarded-for') || 'Unknown';
const userAgent = request.headers.get('user-agent') || 'Unknown';
const referer = request.headers.get('referer') || 'Unknown';
// Process request
const result = await processRequest({
geo: { country, city, region },
ip,
userAgent,
referer,
url: request.url,
});
return NextResponse.json(result, {
status: 200,
headers: {
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
'Content-Type': 'application/json',
'X-Edge-Location': region,
},
});
} catch (error) {
console.error('Edge function error:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json();
// Validate request body
const validationResult = validateRequestBody(body);
if (!validationResult.valid) {
return NextResponse.json(
{ error: 'Invalid request body', details: validationResult.errors },
{ status: 400 }
);
}
// Process POST request
const result = await processPostRequest(body, request);
return NextResponse.json(result, {
status: 201,
headers: {
'Content-Type': 'application/json',
},
});
} catch (error) {
console.error('Edge function POST error:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
async function processRequest(metadata: RequestMetadata): Promise<any> {
// Implement your edge function logic here
return {
message: 'Edge function executed successfully',
metadata,
timestamp: new Date().toISOString(),
};
}
async function processPostRequest(body: any, request: NextRequest): Promise<any> {
// Implement POST logic here
return {
message: 'POST processed successfully',
data: body,
timestamp: new Date().toISOString(),
};
}
function validateRequestBody(body: any): ValidationResult {
// Implement validation logic
return { valid: true, errors: [] };
}
interface RequestMetadata {
geo: {
country: string;
city: string;
region: string;
};
ip: string;
userAgent: string;
referer: string;
url: string;
}
interface ValidationResult {
valid: boolean;
errors: string[];
}Specialized Edge Function Types
1. Geolocation-Based Content Delivery
typescript
// api/edge/geo-content/index.ts
import { NextRequest, NextResponse } from 'next/server';
export const runtime = 'edge';
interface ContentConfig {
[country: string]: {
currency: string;
language: string;
content: string;
pricing: number;
};
}
const contentConfig: ContentConfig = {
'US': {
currency: 'USD',
language: 'en-US',
content: 'Welcome to our US store!',
pricing: 99.99,
},
'GB': {
currency: 'GBP',
language: 'en-GB',
content: 'Welcome to our UK store!',
pricing: 79.99,
},
'DE': {
currency: 'EUR',
language: 'de-DE',
content: 'Willkommen in unserem deutschen Shop!',
pricing: 89.99,
},
};
export async function GET(request: NextRequest) {
const country = request.geo?.country || 'US';
const config = contentConfig[country] || contentConfig['US'];
// Add region-specific headers
const response = NextResponse.json({
country,
...config,
edgeLocation: request.geo?.region,
timestamp: new Date().toISOString(),
});
response.headers.set('Cache-Control', 'public, s-maxage=3600, stale-while-revalidate=86400');
response.headers.set('Vary', 'Accept-Language, CloudFront-Viewer-Country');
response.headers.set('Content-Language', config.language);
return response;
}2. Authentication Edge Function
typescript
// api/edge/auth-check/index.ts
import { NextRequest, NextResponse } from 'next/server';
import { jwtVerify } from 'jose';
export const runtime = 'edge';
const JWT_SECRET = new TextEncoder().encode(
process.env.JWT_SECRET || 'your-secret-key'
);
export async function GET(request: NextRequest) {
try {
// Extract token from header or cookie
const authHeader = request.headers.get('authorization');
const cookieToken = request.cookies.get('auth-token')?.value;
const token = authHeader?.replace('Bearer ', '') || cookieToken;
if (!token) {
return NextResponse.json(
{ error: 'No token provided', authenticated: false },
{ status: 401 }
);
}
// Verify JWT token
const { payload } = await jwtVerify(token, JWT_SECRET);
// Return user info
return NextResponse.json({
authenticated: true,
user: {
id: payload.sub,
email: payload.email,
role: payload.role,
exp: payload.exp,
},
location: {
country: request.geo?.country,
city: request.geo?.city,
},
});
} catch (error) {
console.error('Auth verification failed:', error);
return NextResponse.json(
{ error: 'Invalid token', authenticated: false },
{ status: 401 }
);
}
}
export async function POST(request: NextRequest) {
try {
const { username, password } = await request.json();
// Validate credentials (implement your logic)
const user = await validateCredentials(username, password);
if (!user) {
return NextResponse.json(
{ error: 'Invalid credentials' },
{ status: 401 }
);
}
// Generate JWT token
const token = await generateJWT(user);
const response = NextResponse.json({
success: true,
user: {
id: user.id,
email: user.email,
role: user.role,
},
});
// Set secure cookie
response.cookies.set('auth-token', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 24 * 60 * 60, // 24 hours
});
return response;
} catch (error) {
console.error('Authentication error:', error);
return NextResponse.json(
{ error: 'Authentication failed' },
{ status: 500 }
);
}
}
async function validateCredentials(username: string, password: string) {
// Implement credential validation
// This would typically involve database lookup
return null; // Placeholder
}
async function generateJWT(user: any): Promise<string> {
// Implement JWT generation
// This would use a proper JWT library
return 'jwt-token'; // Placeholder
}3. Data Transformation Edge Function
typescript
// api/edge/transform/index.ts
import { NextRequest, NextResponse } from 'next/server';
export const runtime = 'edge';
interface TransformConfig {
format: 'json' | 'xml' | 'csv';
fields?: string[];
transforms?: Record<string, (value: any) => any>;
}
const transformers = {
// Currency conversion
currency: (value: number, targetCurrency: string = 'USD') => {
const rates = { USD: 1, EUR: 0.85, GBP: 0.73 };
return value * (rates[targetCurrency as keyof typeof rates] || 1);
},
// Date formatting
date: (value: string, format: string = 'ISO') => {
const date = new Date(value);
if (format === 'ISO') return date.toISOString();
if (format === 'US') return date.toLocaleDateString('en-US');
return date.toString();
},
// Text formatting
text: (value: string, caseType: string = 'lower') => {
if (caseType === 'upper') return value.toUpperCase();
if (caseType === 'title') return value.replace(/\w\S*/g, txt =>
txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
);
return value.toLowerCase();
},
};
export async function POST(request: NextRequest) {
try {
const { data, config }: { data: any; config: TransformConfig } = await request.json();
if (!data || !config) {
return NextResponse.json(
{ error: 'Missing data or config' },
{ status: 400 }
);
}
// Apply transformations
const transformedData = await transformData(data, config, request);
// Format output based on requested format
const output = await formatOutput(transformedData, config.format);
const response = new NextResponse(output, {
status: 200,
headers: {
'Content-Type': getContentType(config.format),
'Cache-Control': 'public, s-maxage=300',
},
});
return response;
} catch (error) {
console.error('Transform error:', error);
return NextResponse.json(
{ error: 'Transformation failed' },
{ status: 500 }
);
}
}
async function transformData(data: any, config: TransformConfig, request: NextRequest) {
const country = request.geo?.country || 'US';
// Apply field filtering if specified
if (config.fields && Array.isArray(data)) {
data = data.map(item => {
const filtered: any = {};
config.fields!.forEach(field => {
if (item.hasOwnProperty(field)) {
filtered[field] = item[field];
}
});
return filtered;
});
}
// Apply custom transforms
if (config.transforms) {
Object.entries(config.transforms).forEach(([field, transformFunc]) => {
if (Array.isArray(data)) {
data = data.map(item => ({
...item,
[field]: transformFunc(item[field]),
}));
} else if (data.hasOwnProperty(field)) {
data[field] = transformFunc(data[field]);
}
});
}
// Add geo context
return {
...data,
_meta: {
transformedAt: new Date().toISOString(),
location: country,
edgeRegion: request.geo?.region,
},
};
}
async function formatOutput(data: any, format: string): Promise<string> {
switch (format) {
case 'xml':
return jsonToXml(data);
case 'csv':
return jsonToCsv(data);
case 'json':
default:
return JSON.stringify(data, null, 2);
}
}
function getContentType(format: string): string {
switch (format) {
case 'xml': return 'application/xml';
case 'csv': return 'text/csv';
case 'json':
default: return 'application/json';
}
}
function jsonToXml(data: any): string {
// Simple XML conversion (implement proper XML library for production)
return `<?xml version="1.0" encoding="UTF-8"?><root>${JSON.stringify(data)}</root>`;
}
function jsonToCsv(data: any): string {
// Simple CSV conversion (implement proper CSV library for production)
if (Array.isArray(data) && data.length > 0) {
const headers = Object.keys(data[0]);
const rows = data.map(row => headers.map(header => row[header] || '').join(','));
return [headers.join(','), ...rows].join('\n');
}
return '';
}4. Proxy and Cache Edge Function
typescript
// api/edge/proxy/index.ts
import { NextRequest, NextResponse } from 'next/server';
export const runtime = 'edge';
interface ProxyConfig {
targetUrl: string;
cacheTime: number;
headers?: Record<string, string>;
transformResponse?: boolean;
}
const proxyConfigs: Record<string, ProxyConfig> = {
'api': {
targetUrl: 'https://jsonplaceholder.typicode.com',
cacheTime: 300, // 5 minutes
headers: {
'User-Agent': 'Vercel-Edge-Proxy/1.0',
},
},
'cdn': {
targetUrl: 'https://cdn.example.com',
cacheTime: 3600, // 1 hour
transformResponse: false,
},
};
export async function GET(request: NextRequest) {
try {
const url = new URL(request.url);
const proxyType = url.searchParams.get('type') || 'api';
const targetPath = url.searchParams.get('path') || '';
const config = proxyConfigs[proxyType];
if (!config) {
return NextResponse.json(
{ error: 'Invalid proxy type' },
{ status: 400 }
);
}
// Build target URL
const targetUrl = `${config.targetUrl}${targetPath}`;
// Check cache first (simplified - use proper cache in production)
const cacheKey = `proxy:${targetUrl}`;
// Make request to target
const response = await fetch(targetUrl, {
headers: {
...config.headers,
'X-Forwarded-For': request.headers.get('x-forwarded-for') || '',
'X-Real-IP': request.headers.get('x-real-ip') || '',
},
});
if (!response.ok) {
return NextResponse.json(
{ error: 'Upstream server error' },
{ status: response.status }
);
}
let data;
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
data = await response.json();
// Transform response if configured
if (config.transformResponse) {
data = await transformProxyResponse(data, request);
}
return NextResponse.json(data, {
status: 200,
headers: {
'Cache-Control': `public, s-maxage=${config.cacheTime}, stale-while-revalidate=${config.cacheTime * 2}`,
'X-Proxy-Cache': 'MISS',
'X-Edge-Location': request.geo?.region || 'unknown',
},
});
} else {
// For non-JSON responses, pass through
const blob = await response.blob();
return new NextResponse(blob, {
status: 200,
headers: {
'Content-Type': contentType,
'Cache-Control': `public, s-maxage=${config.cacheTime}`,
},
});
}
} catch (error) {
console.error('Proxy error:', error);
return NextResponse.json(
{ error: 'Proxy request failed' },
{ status: 502 }
);
}
}
async function transformProxyResponse(data: any, request: NextRequest) {
// Add geo context to proxied data
return {
...data,
_proxy: {
timestamp: new Date().toISOString(),
location: request.geo?.country,
region: request.geo?.region,
},
};
}Edge Function Utilities
1. Configuration Management
typescript
// api/edge/[function-name]/config.ts
export interface EdgeFunctionConfig {
cacheTime: number;
rateLimit: {
requests: number;
windowMs: number;
};
geo: {
enabled: boolean;
restrictedCountries?: string[];
};
security: {
corsOrigins: string[];
requireAuth: boolean;
};
}
export const defaultConfig: EdgeFunctionConfig = {
cacheTime: 300, // 5 minutes
rateLimit: {
requests: 100,
windowMs: 60000, // 1 minute
},
geo: {
enabled: true,
},
security: {
corsOrigins: ['*'],
requireAuth: false,
},
};2. Utility Functions
typescript
// api/edge/[function-name]/utils.ts
export function getClientIP(request: NextRequest): string {
return request.headers.get('x-forwarded-for') ||
request.headers.get('x-real-ip') ||
request.ip ||
'unknown';
}
export function generateCacheKey(request: NextRequest, suffix?: string): string {
const url = new URL(request.url);
const baseKey = `${url.pathname}${url.search}`;
return suffix ? `${baseKey}:${suffix}` : baseKey;
}
export function createCorsResponse(
data: any,
origins: string[] = ['*']
): NextResponse {
const response = NextResponse.json(data);
response.headers.set('Access-Control-Allow-Origin', origins.join(', '));
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return response;
}
export function validateGeoRestrictions(
request: NextRequest,
restrictedCountries: string[] = []
): boolean {
const country = request.geo?.country;
return !country || !restrictedCountries.includes(country);
}3. Testing Framework
typescript
// api/edge/[function-name]/__tests__/[function-name].test.ts
import { NextRequest } from 'next/server';
import { GET, POST } from '../index';
// Mock geo data
const createMockRequest = (url: string, options: any = {}) => {
const request = new NextRequest(url, options);
// Mock geo property
Object.defineProperty(request, 'geo', {
value: {
country: 'US',
city: 'New York',
region: 'us-east-1',
},
});
return request;
};
describe('Edge Function', () => {
describe('GET requests', () => {
it('should return geo-based content', async () => {
const request = createMockRequest('http://localhost:3000/api/edge/test');
const response = await GET(request);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.metadata.geo.country).toBe('US');
});
it('should handle missing geo data', async () => {
const request = new NextRequest('http://localhost:3000/api/edge/test');
const response = await GET(request);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.metadata.geo.country).toBe('Unknown');
});
});
describe('POST requests', () => {
it('should validate request body', async () => {
const request = createMockRequest('http://localhost:3000/api/edge/test', {
method: 'POST',
body: JSON.stringify({ invalid: 'data' }),
headers: { 'Content-Type': 'application/json' },
});
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(400);
expect(data.error).toBe('Invalid request body');
});
});
});Performance and Optimization
1. Response Optimization
typescript
// Optimize responses for edge performance
export function optimizeResponse(data: any, request: NextRequest): NextResponse {
const response = NextResponse.json(data);
// Set appropriate cache headers
const cacheTime = getCacheTime(request.url);
response.headers.set(
'Cache-Control',
`public, s-maxage=${cacheTime}, stale-while-revalidate=${cacheTime * 2}`
);
// Add compression hints
response.headers.set('Content-Encoding', 'gzip');
// Add performance headers
response.headers.set('X-Edge-Location', request.geo?.region || 'unknown');
return response;
}
function getCacheTime(url: string): number {
// Dynamic cache time based on URL patterns
if (url.includes('/static/')) return 3600; // 1 hour
if (url.includes('/api/')) return 60; // 1 minute
return 300; // 5 minutes default
}2. Error Handling
typescript
export function createErrorResponse(
error: unknown,
request: NextRequest
): NextResponse {
console.error('Edge function error:', error);
// Log error with context
const errorContext = {
url: request.url,
method: request.method,
country: request.geo?.country,
timestamp: new Date().toISOString(),
};
// Return appropriate error response
return NextResponse.json(
{
error: 'Internal server error',
requestId: generateRequestId(),
},
{
status: 500,
headers: {
'X-Error-Context': JSON.stringify(errorContext),
},
}
);
}
function generateRequestId(): string {
return Math.random().toString(36).substr(2, 9);
}Generate comprehensive edge function implementation with the requested features, proper TypeScript types, error handling, and optimization patterns.