Error Handling
Error handling is a crucial part of any application that communicates with an API. In Hyper-Fetch, error handling is
designed to be robust, type-safe, and developer-friendly. Unlike traditional promise-based libraries that might reject
on failure, Hyper-Fetch requests never reject. Instead, they return an error object alongside the data, ensuring
that you can handle both success and failure states gracefully.
This approach simplifies your async code by avoiding the need for try...catch blocks around your requests.
- How Hyper-Fetch handles errors without rejecting promises.
- How to define a global error type for all requests made with a client instance.
- How to specify local error types for individual requests.
- How to access and differentiate between error types in your application.
Global Errors
A global error type provides a consistent error structure for all requests initiated from a Client instance. This is
useful for defining a standard error shape that your API returns, such as a generic error message and a status code.
You can define the global error type using generics when creating your client.
import { Client } from "@hyper-fetch/core";
// 1. Define a global error type
type GlobalErrorType = {
message: string;
status: number;
timestamp: string;
};
// 2. Create a client with the global error type
const clientWithGlobalError = new Client<{ error: GlobalErrorType }>({
url: "https://jsonplaceholder.typicode.com",
});
const requestWithGlobalError = clientWithGlobalError.createRequest()({
endpoint: "/users/999", // This will cause a 404 error
method: "GET",
});
// 3. Example usage
const { data, error } = await requestWithGlobalError.send();
// `error` will be `GlobalErrorType | null`
if (error) {
// Our mock server returns a plain object on 404
// but in a real scenario, this would be typed
console.log("Error occurred", { error });
}
This setup ensures that any request created from this client instance will have its error property typed as
GlobalErrorType | null, providing type safety across your application.
Local Errors
While global errors are great for consistency, some endpoints may return different error structures. For example, a form submission endpoint might return detailed validation errors for each field. Hyper-Fetch allows you to define a local error type for specific requests.
This local error type will be combined with the global error type, creating a union type for the error property.
import { Client } from "@hyper-fetch/core";
// Let's assume this client is defined in our app
type GlobalErrorType = {
message: string;
status: number;
};
const clientForLocalError = new Client<{ error: GlobalErrorType }>({
url: "https://jsonplaceholder.typicode.com",
});
// 1. Define a local error type for a specific endpoint
type UserValidationError = {
errors: {
email?: string[];
password?: string[];
};
};
// 2. Create a request and specify the local error type
// Note that we are passing the local error to the `error` generic
const signUpRequest = clientForLocalError.createRequest<{ error: UserValidationError }>()({
endpoint: "/users",
method: "POST",
payload: { email: "not-an-email" },
});
// 3. Example usage
const { data: signUpData, error: signUpError } = await signUpRequest.send();
// The `error` type is now a union of the global and local error types:
// `GlobalErrorType | UserValidationError | null`
console.log("Response from 'local error' example", { signUpData, signUpError });
if (signUpError) {
// You can now narrow down the error type
if ("errors" in signUpError) {
// This is the UserValidationError
console.error("Validation failed (simulated):", { email: "Invalid email format" });
} else {
// This is the GlobalErrorType
console.error("A global error occurred:", signUpError.message);
}
}
By defining local errors, you can handle endpoint-specific error responses with full type safety, making your error-handling logic more robust and predictable.
Error Type Narrowing
When a request has both global and local error types, the resulting error property is a union of these types. To
handle them correctly, you'll need to perform type narrowing. A common way to do this is by checking for a property that
is unique to one of the error types.
// Let's imagine we received an error from the signUp request
const receivedError: GlobalErrorType | UserValidationError | null = {
errors: { email: ["Enter a valid email."] },
};
if (receivedError) {
if ("errors" in receivedError) {
// TypeScript now knows `error` is of type `UserValidationError`
console.log(receivedError.errors.email);
} else {
// Here, `error` must be `GlobalErrorType`
console.log(receivedError.message);
}
}
This pattern allows you to write specific error handling logic for different failure scenarios, all while benefiting from TypeScript's static analysis.
You've learned how to effectively handle errors in Hyper-Fetch!
- You can define global and local error types to match your API's responses.
- You can access error information from requests without using
try...catch. - You can use type narrowing to handle different error types safely.
