Zod's popularity has been and is really picking up right now and I'm sure there's a large number of tutorials and explainers out there, but today we'll attempt to precisely go over, arguably the greatest and one of my personal favourite use cases, API Response Typesafety.
Not only would we like Typesafety, but sometimes we don't need or want all the data we get back from our API's... So let's do both with Zod
TL:DR If you haven't heard of Zod, it's a small, zero dependency, validation library. Zod plays extremely nice with TypeScript, but it works with plain JavaScript too!
I've implemented this in a single file in the new stable Next.JS 13.4 app directory, to demonstrate this, but you might want to split these up.
Install Zod
npm install zod
or yarn add zod
First import z from Zod and define a validation schema. This defines an array of one or more objects that have an id, title, date and uri, which are all strings.
import { z } from "zod";
const parsedResults = z.array(
z.object({
id: z.string(),
title: z.string(),
date: z.string(),
uri: z.string(),
})
);
We can also use our validation schema to infer a TypeScript type for extra typesafety too.
type ParsedEpisodes = z.infer<typeof parsedResults>;
To demonstrate, we'll use the Learn with Jason API
and as you can see, we get back quite a bit of information for each episode/
"host": {
"image": "https://cdn.sanity.io/images/vnkupgyb/production/11b8e51d90b26838de71904294430279b7099995-700x700.jpg",
"name": "Jason Lengstorf",
"twitter": "jlengstorf"
},
"tags": [
{
"label": "Testing",
"slug": "testing",
"uri": "https://www.learnwithjason.dev/topic/testing"
}
],
"id": "83a25cf4-dc5a-4b6b-a77e-375822dc815c",
"title": "Component-driven development with Faker.js",
"slug": "component-driven-development-with-faker-js",
"description": "Using realistic data for dev helps create more resilient code in less time. Jessica Sachs will teach us how to use component-driven dev + Faker.JS to build better code faster.",
"uri": "https://www.learnwithjason.dev/component-driven-development-with-faker-js",
"date": "2023-06-16T16:30:00.000Z",
"guest": {
"image": "https://cdn.sanity.io/images/vnkupgyb/production/02354409cec8d403fa78daf21e9c748e98eb699b-2316x2316.jpg",
"name": "Jessica Sachs",
"twitter": "_jessicasachs"
}
We only what the id, title, date and uri in the validation schema we defined earlier, so we pass the returned response from our API request, into the safeParse method of the validation schema
const res = await fetch(`https://www.learnwithjason.dev/api/v2/schedule`, {
method: "GET",
});
let episodes = await res.json();
const validatedData = parsedResults.safeParse(episodes);
let parsedEpisodes: ParsedEpisodes;
if (validatedData.success) {
parsedEpisodes = validatedData.data;
console.log(parsedEpisodes);
} else {
console.log("some error");
}
Using the safeParse method, we get back an object with a success boolean and our data if there is sufficient data to satisfy our validation schema.
{
"success": true,
"data": [
{
"id": "da1679e9-9834-4ee5-8e29-199ce6cca463",
"title": "Redux in 2023: What you need to know",
"date": "2023-06-08T16:30:00.000Z",
"uri": "https://www.learnwithjason.dev/redux-in-2023-what-you-need-to-know"
},
{
"id": "83a25cf4-dc5a-4b6b-a77e-375822dc815c",
"title": "Component-driven development with Faker.js",
"date": "2023-06-16T16:30:00.000Z",
"uri": "https://www.learnwithjason.dev/component-driven-development-with-faker-js"
}
]
}
As we have defined and inferred our TypeScript type above that we expected to get back from the API, we can use it to get our nice auto complete elsewhere in our code e.g like I have done by simply mapping over the typed data.
<main className="flex min-h-screen flex-col items-center p-24">
{parsedEpisodes!.map((ep) => (
<div
className="w-3/5 h-[150px] ring ring-gray-950 rounded-md my-5 flex flex-col items-center"
key={ep.id}
>
<div>{ep.title}</div>
<div>{ep.title}</div>
</div>
))}
</main>
There's a whole lot more that Zod can do, but this is my powerful and favourite use case.
Top comments (0)