zod validation setup

This commit is contained in:
Oliver Bryan
2026-01-13 15:25:18 +00:00
parent c94c714548
commit 5185fdddd9
3 changed files with 63 additions and 1 deletions

View File

@@ -20,6 +20,7 @@
"jsonwebtoken": "^9.0.3",
"pg": "^8.16.3",
"sharp": "^0.34.5",
"zod": "^3.23.8",
},
"devDependencies": {
"@types/bcrypt": "^6.0.0",

View File

@@ -33,6 +33,7 @@
"drizzle-orm": "^0.45.0",
"jsonwebtoken": "^9.0.3",
"pg": "^8.16.3",
"sharp": "^0.34.5"
"sharp": "^0.34.5",
"zod": "^3.23.8"
}
}

View File

@@ -0,0 +1,60 @@
import type { ApiError } from "@issue/shared";
import type { z } from "zod";
type ZodSchema<T> = z.ZodSchema<T>;
type ZodError = z.ZodError;
export function formatZodError(error: ZodError): ApiError {
const details: Record<string, string[]> = {};
for (const issue of error.issues) {
const path = issue.path.join(".") || "_root";
if (!details[path]) details[path] = [];
details[path].push(issue.message);
}
return {
error: "validation failed",
code: "VALIDATION_ERROR",
details,
};
}
export function errorResponse(error: string, code?: string, status = 400): Response {
const body: ApiError = { error, code };
return Response.json(body, { status });
}
export async function parseJsonBody<T>(
req: Request,
schema: ZodSchema<T>,
): Promise<{ data: T } | { error: Response }> {
let body: unknown;
try {
body = await req.json();
} catch {
return {
error: Response.json({ error: "invalid JSON", code: "INVALID_JSON" } satisfies ApiError, {
status: 400,
}),
};
}
const result = schema.safeParse(body);
if (!result.success) {
return {
error: Response.json(formatZodError(result.error), { status: 400 }),
};
}
return { data: result.data };
}
export function parseQueryParams<T>(url: URL, schema: ZodSchema<T>): { data: T } | { error: Response } {
const params = Object.fromEntries(url.searchParams);
const result = schema.safeParse(params);
if (!result.success) {
return {
error: Response.json(formatZodError(result.error), { status: 400 }),
};
}
return { data: result.data };
}