Errors and Retries
Handling errors
Requests reject with a RequestError for network failures, unacceptable status codes, response parsing failures, and validation failures.
import { getErrorResponse, isRequestError } from "api-def";
try {
const response = await fetchHealthCheck.submit({});
console.log(response.data);
} catch (error: unknown) {
if (isRequestError(error)) {
const response = getErrorResponse(error);
console.error(error.code, error.attempts, response?.status);
}
}
Useful error codes include:
request/network-errorfor a failed network operation.request/host-name-not-foundwhen the host cannot be resolved.request/invalid-statuswhen the response status is not acceptable.request/mismatch-response-typewhen the response cannot be parsed as requested.validation/query-validate-error,validation/body-validate-error, andvalidation/response-validate-errorfor schema failures.
The error also includes the attempted request details and the last response when one was available.
Retry configuration
Set retry to a number for a simple retry count, or pass a RetryOptions object for control over retry timing and eligibility:
import { Api, isRequestError } from "api-def";
const API = new Api({
name: "My Backend",
baseUrl: "https://api.example.com",
defaultRequestConfig: {
retry: {
maxAttempts: 3,
minDelay: 200,
maxDelay: 1000,
shouldRetry: (error) => isRequestError(error) && error.response == null,
},
},
});
maxAttempts is the number of retries after the initial attempt. shouldRetry returning true allows the retry; returning false stops retrying. The final failure is then observed by error middleware.
By default, failed requests are retried when retry is enabled. Supply shouldRetry when only particular failures should be retried, such as transient network errors.
Middleware can return { type: "retry" } from the attemptError event to force a retry. Use that carefully because forced retries can loop indefinitely if the middleware never stops returning the result.
Acceptable statuses
Successful responses default to 200 through 299. Override that per endpoint or request:
const redirect = API.endpoint().build({
id: "redirect",
method: "get",
path: "/redirect",
defaultRequestConfig: {
acceptableStatus: [[301, 302], 200],
},
});
Debugging async requests
When console.createTask is available, api-def automatically tags each submission so DevTools can link middleware, retry attempts, and XHR/WebSocket connection callbacks back to the original .submit() caller. Task names use the method and endpoint path template, such as api-def GET /users/:id, without query strings or fragments.
This needs no configuration and falls back to normal execution when the API is unavailable. It improves DevTools async stack traces; it does not add distributed tracing headers or change request errors.
Low-level retry helper
The exported retry helper is useful outside request submission:
import { retry } from "api-def";
const result = await retry(
async (bail, attempt) => {
if (attempt === 2) return "ready";
if (attempt > 3) bail(new Error("not ready"));
throw new Error("try again");
},
{ retries: 3, minTimeout: 100, maxTimeout: 500 },
);