DEV Community

Cover image for Autocompleting API Responses for the zod curious
Aodhan Hamilton
Aodhan Hamilton

Posted on • Updated on

Autocompleting API Responses for the zod curious

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(),
    })
);
Enter fullscreen mode Exit fullscreen mode

We can also use our validation schema to infer a TypeScript type for extra typesafety too.

type ParsedEpisodes = z.infer<typeof parsedResults>;
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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");
    }
Enter fullscreen mode Exit fullscreen mode

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"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

VSCode type inferance example screenshot

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>
Enter fullscreen mode Exit fullscreen mode

There's a whole lot more that Zod can do, but this is my powerful and favourite use case.

Top comments (0)