Error Handling
HttpError
class HttpError extends Error { status: number | null // null for network/abort/timeout body: unknown // parsed response body response: Response | null headers: Record<string, string> | null // null when no response was received request: OutgoingRequest | null // the outgoing request that produced this error isAborted: boolean isTimeout: boolean isNetwork: boolean
// Status predicates — getters, no () needed get isClientError(): boolean // 4xx get isServerError(): boolean // 5xx get isUnauthorized(): boolean // 401 get isForbidden(): boolean // 403 get isNotFound(): boolean // 404 get isValidationError(): boolean // 422 get isRateLimited(): boolean // 429
// Omits `request` (may contain Authorization / Cookie headers). toJSON(): Record<string, unknown>}HttpResult
type HttpResult<T> = | { data: T; error: null; status: number; response: Response; headers: Record<string, string>; request: OutgoingRequest } | { data: null; error: HttpError; status: number | null; response: Response | null; headers: Record<string, string> | null; request: OutgoingRequest }Default pattern — destructure result
const { data, error } = await http.get<User>('/users/1');
if (error) { if (error.isNotFound) console.warn('User not found'); else if (error.isUnauthorized) redirect('/login'); else if (error.isValidationError) showFormErrors(error.body); else if (error.isAborted) { /* request was cancelled — ignore */ } else showGenericError(error.message); return;}
// data is User here — TypeScript knows error is nullconsole.log(data.name);Validation errors (422) — Laravel-style
const { data, error } = await usersResource.create(formData);
if (error?.isValidationError) { // error.body is whatever the server returned, e.g.: // { errors: { name: ['The name field is required.'], ... } } const { errors } = error.body as { errors: Record<string, string[]> }; for (const [field, messages] of Object.entries(errors)) { setFieldError(field, messages[0]); }}Opt-in throw mode
try { const { data } = await http.get('/users/1', { throw: true });} catch (err) { if (err instanceof HttpError && err.isNotFound) { // handle 404 }}Type narrowing
The HttpResult<T> discriminated union narrows automatically:
const result = await http.get<User>('/me');
if (result.error) { result.data // null — TypeScript knows result.error // HttpError} else { result.data // User — TypeScript knows result.error // null result.headers // Record<string, string>}