40 lines
1.5 KiB
TypeScript
40 lines
1.5 KiB
TypeScript
import type { ZodType } from "zod";
|
|
|
|
/** Applikationsfel med HTTP-status och maskinläsbar kod. */
|
|
export class ApiError extends Error {
|
|
constructor(
|
|
public readonly statusCode: number,
|
|
public readonly code: string,
|
|
message: string,
|
|
public readonly details?: unknown,
|
|
) {
|
|
super(message);
|
|
this.name = "ApiError";
|
|
}
|
|
}
|
|
|
|
export const errors = {
|
|
badRequest: (msg: string, details?: unknown) => new ApiError(400, "BAD_REQUEST", msg, details),
|
|
unauthorized: (msg = "Ogiltig eller saknad autentisering") =>
|
|
new ApiError(401, "UNAUTHORIZED", msg),
|
|
forbidden: (msg = "Åtkomst nekad") => new ApiError(403, "FORBIDDEN", msg),
|
|
notFound: (msg = "Resursen finns inte") => new ApiError(404, "NOT_FOUND", msg),
|
|
conflict: (msg: string) => new ApiError(409, "CONFLICT", msg),
|
|
quotaExceeded: (msg: string) => new ApiError(429, "QUOTA_EXCEEDED", msg),
|
|
paymentRequired: (msg: string) => new ApiError(402, "PREMIUM_REQUIRED", msg),
|
|
internal: (msg = "Internt fel") => new ApiError(500, "INTERNAL", msg),
|
|
};
|
|
|
|
/**
|
|
* Validera indata mot ett Zod-schema. Kastar 400 med detaljer vid fel.
|
|
* Medvetet vald i stället för type-provider: färre rörliga delar i
|
|
* major-versionsgränser, samma säkerhet (se docs/beslutslogg.md D-011).
|
|
*/
|
|
export function parse<T>(schema: ZodType<T>, data: unknown, what = "indata"): T {
|
|
const result = schema.safeParse(data);
|
|
if (!result.success) {
|
|
throw new ApiError(400, "VALIDATION_ERROR", `Ogiltig ${what}`, result.error.issues);
|
|
}
|
|
return result.data;
|
|
}
|