Guide - Validation
Data validation is a critical aspect of building robust and reliable applications. It ensures that the data flowing through your application, both from the server and from user inputs, adheres to a predefined structure and set of rules. Without proper validation, you risk encountering unexpected errors, data corruption, and security vulnerabilities.
Hyper Fetch provides a powerful and flexible way to handle validation by leveraging mappers. When a mapper throws an error, Hyper Fetch catches it and treats it as a request error. This mechanism allows you to integrate any validation library, like Zod, to validate request and response data seamlessly.
- How to perform response data validation to ensure API responses are correct.
- How to implement request data validation to prevent sending invalid data to the server.
- Using mappers for centralized and reusable validation logic.
- How to integrate with popular validation libraries like Zod.
Response Validation
Response validation is the process of verifying that the data received from the server matches the expected format. This is crucial for preventing runtime errors in your application when an API returns an unexpected response.
We can use the .setResponseMapper() method on a request to inspect the raw response and validate its structure. If the
validation fails, we can throw an error, which Hyper Fetch will catch and place in the error state of the request.
Let's see an example using zod:
import { z } from "zod";
const UserSchema = z.object({
id: z.number(),
name: z.string(),
username: z.string(),
email: z.string().email(),
});
// We can infer the type from the schema
type User = z.infer<typeof UserSchema>;
const getUser = client
.createRequest<{ response: User }>()({
method: "GET",
endpoint: "users/:userId",
})
.setResponseMapper((response) => {
if (response.data) {
// Validate the parsed response data against our schema.
// If parsing fails, Zod throws an error that Hyper Fetch will catch.
UserSchema.parse(response.data);
}
// Return the response object (possibly with transformed data)
return response;
});
// Usage
(async () => {
const { data, error } = await getUser.setParams({ userId: 1 }).send();
if (error) {
// Here error will be an instance of ZodError if validation failed
console.error(error);
} else {
console.log(data);
}
})();
In this example, if the data received from /users/:userId does not conform to the UserSchema,
UserSchema.parse(data) will throw a ZodError. Hyper Fetch will catch this error, stop the request processing, and
you will get the ZodError instance in the error property of the request's return value.
Request Validation
Just as we validate responses, it's equally important to validate data before sending it to the server. This prevents invalid data from reaching your API, which can help avoid unnecessary API calls and potential server-side errors.
Request validation can be implemented using the .setRequestMapper() method. This mapper receives the request instance
before it's sent, allowing you to inspect and validate its payload.
Here's how you can validate a request payload with zod:
import { z } from "zod";
const PostSchema = z.object({
title: z.string().min(3, "Title must be at least 3 characters long"),
body: z.string().min(10, "Body must be at least 10 characters long"),
userId: z.number(),
});
type PostSchema = z.infer<typeof PostSchema>;
// The second generic to createRequest is the payload type
const createPost = client
.createRequest<{ response: Post; payload: PostSchema }>()({
method: "POST",
endpoint: "/posts",
})
.setRequestMapper((request) => {
// We access the data payload of the request
const dataToValidate = request.data;
// We parse it against our schema
PostSchema.parse(dataToValidate); // This will throw if validation fails
// If validation is successful, we must return the request instance
return request;
});
// --- Usage ---
// Example 1: Invalid data
const { error: validationError } = await createPost.send({
payload: { title: "A", body: "Short", userId: 1 },
});
if (validationError) {
// The request was not sent, and we get a ZodError
console.log(validationError.errors);
}
// Example 2: Valid data
const { data, error } = await createPost.send({
payload: { title: "A valid title", body: "A much longer and valid body", userId: 1 },
});
if (error) {
console.error("Request failed:", error);
} else {
// The request was sent successfully
console.log("Post created:", data);
}
If you call createPost.send() with data that doesn't match PostSchema, the .setRequestMapper() will throw an
error. The request will be aborted before it is sent, and the error will be returned.
- You can perform response validation using
.setResponseMapper()to ensure data from the server is safe to use. - You can implement request validation with
.setRequestMapper()to prevent sending invalid data. - You know how to use mappers to integrate third-party validation libraries like Zod.
- You can create type-safe requests where the types are inferred directly from your validation schemas.
