/ src / lib / server / safe-parse-body.ts
safe-parse-body.ts
 1  import { NextResponse } from 'next/server'
 2  import { z } from 'zod'
 3  
 4  import { formatZodError } from '@/lib/validation/schemas'
 5  
 6  type SafeResult<T> = { data: T; error?: never } | { data?: never; error: NextResponse }
 7  
 8  /**
 9   * Wraps `req.json()` so malformed/empty bodies return a 400
10   * instead of throwing an unhandled error (500).
11   */
12  export async function safeParseBody<T = Record<string, unknown>>(
13    req: Request,
14    schema?: z.ZodType<T>,
15  ): Promise<SafeResult<T>> {
16    let raw: unknown
17    try {
18      raw = await req.json()
19    } catch {
20      return { error: NextResponse.json({ error: 'Invalid or missing request body' }, { status: 400 }) }
21    }
22  
23    if (!schema) {
24      return { data: raw as T }
25    }
26  
27    const parsed = schema.safeParse(raw)
28    if (!parsed.success) {
29      return { error: NextResponse.json(formatZodError(parsed.error), { status: 400 }) }
30    }
31  
32    return { data: parsed.data }
33  }