Error Handling
Handle API errors with typed error classes
Error Handling
The Zodra client parses error responses into typed error classes, making it easy to handle different failure modes.
Error classes
ZodraClientError
Base error for all HTTP errors. Thrown for unexpected status codes:
import { ZodraClientError } from "@zodra/client";
try {
await api.products.show({ id: "..." });
} catch (error) {
if (error instanceof ZodraClientError) {
console.log(error.status); // HTTP status code
console.log(error.body); // raw response body
console.log(error.message); // error message
}
}ZodraFieldError
Thrown for field-level validation errors (HTTP 422 with errors key):
import { ZodraFieldError } from "@zodra/client";
try {
await api.products.create({ name: "", price: -1 });
} catch (error) {
if (error instanceof ZodraFieldError) {
console.log(error.errors);
// { name: ["must be at least 1 character"], price: ["must be >= 0"] }
// Display field-specific errors
for (const [field, messages] of Object.entries(error.errors)) {
console.log(`${field}: ${messages.join(", ")}`);
}
}
}ZodraBusinessError
Thrown for business logic errors (response with error.code):
import { ZodraBusinessError } from "@zodra/client";
try {
await api.orders.confirm({ id: "..." });
} catch (error) {
if (error instanceof ZodraBusinessError) {
console.log(error.code); // "invalid_transition"
console.log(error.message); // "Order cannot be confirmed"
console.log(error.status); // 422
}
}ZodraValidationError
Thrown when client-side validation fails (when validateParams or validateResponse is enabled):
import { ZodraValidationError } from "@zodra/client";
const api = createApiClient({
baseUrl: "/api/v1",
contracts,
validateParams: true,
});
try {
// @ts-expect-error — bypassing TypeScript with wrong types
await api.products.create({ name: 123 });
} catch (error) {
if (error instanceof ZodraValidationError) {
console.log(error.issues); // Zod validation issues
}
}Handling all error types
import {
ZodraFieldError,
ZodraBusinessError,
ZodraValidationError,
ZodraClientError,
} from "@zodra/client";
try {
await api.orders.create(orderData);
} catch (error) {
if (error instanceof ZodraFieldError) {
// Show field errors in the form
setFormErrors(error.errors);
} else if (error instanceof ZodraBusinessError) {
// Show business error message
showToast(error.message);
} else if (error instanceof ZodraValidationError) {
// Client-side validation failed (bug in your code)
console.error("Validation failed:", error.issues);
} else if (error instanceof ZodraClientError) {
// Other HTTP errors (500, 403, etc.)
showToast(`Request failed: ${error.status}`);
}
}Error hierarchy
All error classes extend ZodraClientError, which extends Error:
Error
└── ZodraClientError (status, body)
├── ZodraValidationError (issues)
├── ZodraFieldError (errors)
└── ZodraBusinessError (code)