import { NextResponse } from 'next/server' import { ZodError, type z } from 'zod' /** Throwable API error with HTTP status code */ export class ApiError extends Error { status: number constructor(message: string, status: number = 400) { super(message) this.name = 'ApiError' this.status = status } } /** Standard success response: { ok: true, ...data } */ export function apiOk(data?: Record) { return NextResponse.json({ ok: true, ...data }) } /** Standard error response: { error: message } with given status */ export function apiError(message: string, status: number = 400) { return NextResponse.json({ error: message }, { status }) } /** Validation error from Zod: { error: fieldErrors } with 400 */ export function apiValidationError(zodError: z.ZodError) { return NextResponse.json({ error: zodError.flatten().fieldErrors }, { status: 400 }) } /** Centralized error handler for API routes */ export function handleApiError(error: unknown): Response { if (error instanceof ApiError) { return apiError(error.message, error.status) } if (error instanceof ZodError) { return apiValidationError(error) } // Prisma P2025 "Record not found" if ( error instanceof Error && (error.constructor.name === 'PrismaClientKnownRequestError' || error.name === 'PrismaClientKnownRequestError') && (error as Error & { code?: string }).code === 'P2025' ) { return apiError('Not found', 404) } console.error( '[API error]', error instanceof Error ? { message: error.message, name: error.name } : error, ) return apiError('Internal server error', 500) }