import { NextRequest, NextResponse } from 'next/server'; import { z, ZodSchema } from 'zod'; import { db } from '@/lib/db'; // ============================================================ // Standard API Response // ============================================================ export interface ApiResponse { success: boolean; data?: T; error?: string; meta?: { page: number; limit: number; total: number; totalPages: number; }; } export function successResponse(data: T, status = 200): NextResponse> { return NextResponse.json({ success: true, data }, { status }); } export function errorResponse(error: string, status = 400): NextResponse { return NextResponse.json({ success: false, error }, { status }); } export function paginatedResponse( data: T[], page: number, limit: number, total: number ): NextResponse> { return NextResponse.json({ success: true, data, meta: { page, limit, total, totalPages: Math.ceil(total / limit), }, }); } // ============================================================ // Pagination Helper // ============================================================ export interface PaginationParams { page: number; limit: number; skip: number; } export function getPagination(request: NextRequest): PaginationParams { const url = new URL(request.url); const page = Math.max(1, parseInt(url.searchParams.get('page') || '1')); const limit = Math.min(100, Math.max(1, parseInt(url.searchParams.get('limit') || '20'))); const skip = (page - 1) * limit; return { page, limit, skip }; } // ============================================================ // Sorting Helper // ============================================================ export function getSorting(request: NextRequest, defaultField = 'createdAt', defaultOrder: 'asc' | 'desc' = 'desc') { const url = new URL(request.url); const sortField = url.searchParams.get('sort') || defaultField; const sortOrder = url.searchParams.get('order') || defaultOrder; return { [sortField]: sortOrder } as Record; } // ============================================================ // Filtering Helper // ============================================================ export function getFilters(request: NextRequest, allowedFilters: string[]): Record { const url = new URL(request.url); const filters: Record = {}; for (const key of allowedFilters) { const value = url.searchParams.get(key); if (value) filters[key] = value; } return filters; } // ============================================================ // Validation Helper // ============================================================ export function validateBody(schema: ZodSchema, body: unknown): { data?: T; error?: string } { try { const data = schema.parse(body); return { data }; } catch (err) { if (err instanceof z.ZodError) { const messages = err.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', '); return { error: messages }; } return { error: 'Invalid input' }; } } // ============================================================ // Request Body Helper // ============================================================ export async function getBody(request: NextRequest): Promise { try { return await request.json(); } catch { return {}; } } // ============================================================ // Simple Rate Limiter (in-memory) // ============================================================ const rateLimitMap = new Map(); export function rateLimit(key: string, maxRequests = 60, windowMs = 60000): boolean { const now = Date.now(); const entry = rateLimitMap.get(key); if (!entry || now > entry.resetTime) { rateLimitMap.set(key, { count: 1, resetTime: now + windowMs }); return true; } if (entry.count >= maxRequests) { return false; } entry.count++; return true; } // ============================================================ // API Handler Wrapper // ============================================================ export function apiHandler( handler: (request: NextRequest, context?: { params: Promise> }) => Promise ) { return async (request: NextRequest, context?: { params: Promise> }) => { try { // Rate limiting const ip = request.headers.get('x-forwarded-for') || 'unknown'; if (!rateLimit(ip)) { return errorResponse('Rate limit exceeded', 429); } return await handler(request, context); } catch (error) { console.error('API Error:', error); const message = error instanceof Error ? error.message : 'Internal server error'; return errorResponse(message, 500); } }; }