Error Handling
Learn more about how to handle errors in CallApi
It's prevalent knowledge that making network requests is inherently risky. Things can go wrong for many reasons:
- The server might be down.
- The server might respond with an error status like 404 Not Found (the resource doesn't exist) or 500 Internal Server Error (something broke on the server).
- There might be a network issue.
- The request might timeout.
- The response data might not be in the format you expected (e.g., not valid JSON).
When using the standard browser fetch API, handling these failures can sometimes be a bit clunky due to the following reasons:
- Network errors throw one type of error
- Non-2xx HTTP responses don't throw errors by default (you have to check response.ok)
- Parsing errors might throw yet another type
This can lead to complex if/else chains and unwieldy try...catch blocks just to figure out what went wrong.
CallApi aims to make dealing with these failures much more predictable and convenient.
Structure of the error property
As introduced in the Getting Started guide, CallApi wraps responses in a result object with three key properties: data, error, and response.
When something goes wrong, the error property contains a structured object with:
-
name: A string identifying the type of error (e.g.,
'HTTPError','ValidationError','TypeError','TimeoutError', ...etc). -
message: A brief description of what went wrong:
- For HTTP errors: The error message from the server, or if not provided, falls back to the
defaultHTTPErrorMessageoption - For validation errors: A formatted error message derived from the validation issues array
- For non-HTTP errors: The error message from the JavaScript error object that caused the error
- For HTTP errors: The error message from the server, or if not provided, falls back to the
-
errorData: The detailed error information:
- For HTTP errors: It is set to the
parsed error response from the API - For validation errors: It is set to the
validation issues array - For non-HTTP errors: It is set to
false
- For HTTP errors: It is set to the
-
originalError: The original error object that caused the error:
- For HTTP errors:
HTTPError - For validation errors:
ValidationError - For non-HTTP errors: The underlying javascript error object (e.g.,
TypeError,DOMException, etc.)
- For HTTP errors:
import { } from "@zayne-labs/callapi";
const { } = await ("https://my-api.com/api/v1/session");
Hover over the error object to see the typeHandling HTTP Errors
One of the most common types of errors you'll encounter is when the server responds with a status code outside the 200-299 range (like 400, 401, 403, 404, 500, 503, etc.). Standard fetch doesn't throw an error for these responses.
CallApi, by default, wraps these responses in an HTTPError.
You can customize the error response data type by providing a second generic type argument to callApi.
import { } from "@zayne-labs/callapi";
type = {
: <string | string[]>;
: string;
};
const { } = await <unknown, >("/api/endpoint");
if () {
.(.);
}Since the error property is a discriminated union, you can use the isHTTPError utility from @zayne-labs/callapi/utils to check if it's an HTTP error:
import { } from "@zayne-labs/callapi";
import { } from "@zayne-labs/callapi/utils";
type = {
: boolean;
: number;
: string;
: string;
};
type = {
?: <string | string[]>;
?: string;
};
const { , } = await <, >("https://my-api.com/api/v1/session");
if (()) {
.();
.(.); // 'HTTPError'
.(.);
.(.); // Will be set to the error response data
}Handling Validation Errors
When schema validation fails, CallApi wraps the failure in a ValidationError. See the Validation section for details.
You can use the isValidationError utility to check specifically for this error type:
import { } from "@zayne-labs/callapi";
import { } from "@zayne-labs/callapi/utils";
import { } from "zod";
const = .({
: .(),
: .(),
: .(),
: .(),
});
const { , } = await ("https://my-api.com/api/v1/session", {
: {
: ,
},
});
if (()) {
.(.); // 'ValidationError'
.(.); // Validation issues array
}Throwing Errors
Set throwOnError: true to throw errors instead of returning them—useful for libraries expecting promise rejection:
import { } from "@zayne-labs/callapi";
import { , } from "@zayne-labs/callapi/utils";
type = {
: boolean;
: number;
: string;
: string;
};
type = {
?: <string | string[]>;
?: string;
};
try {
const { } = await <>("https://my-api.com/api/v1/session", {
: true,
});
} catch () {
if (<>()) {
.(.);
}
if (()) {
.(.);
}
}Conditional throwing:
You can also pass a function to throwOnError for conditional throwing based on the error context:
import { } from "@zayne-labs/callapi";
import { , } from "@zayne-labs/callapi/utils";
// Only throw for authentication errors
const = await ("https://my-api.com/api/v1/session", {
: ({ }) => ?. === 401,
});
// Throw for client errors (user mistakes) but not server errors (temporary issues)
const = await ("https://my-api.com/api/users", {
: ({ }) => {
if (!) {
return false;
}
return . >= 400 && . < 500;
},
});
// Complex conditional logic based on error type and context
const = await ("https://my-api.com/api/sensitive", {
: ({ , , }) => {
// Always throw validation errors - data integrity is critical
if (()) {
return true;
}
// Throw HTTP errors for sensitive endpoints
if (() && ?. === 403 && .?.("/sensitive")) {
return true;
}
// Throw rate limiting errors during business hours (handle differently off-hours)
if (?. === 429) {
const = new ().();
return >= 9 && <= 17;
}
// Return other errors in result object
return false;
},
});Type Narrowing
The data and error properties form a discriminated union—if one is present, the other is null. TypeScript automatically narrows types after error checks:
import { } from "@zayne-labs/callapi";
import { } from "@zayne-labs/callapi/utils";
type = {
: boolean;
: number;
: string;
: string;
};
type = {
?: <string | string[]>;
?: string;
};
const { , } = await <, >("https://my-api.com/api/v1/session");
if (()) {
.();
} else if () {
.();
} else {
.(); // TypeScript knows data is not null
}Error Recovery with refetch
Sometimes you want to silently recover from an error and retry the request (e.g., refreshing an expired auth token). CallApi provides a refetch() function within the options object of your error hooks for this purpose.
For common recovery patterns like token refreshing, see the Authorization guide.
Last updated on