ReactHustle

How to Add Typescript Types to NextJS API Request Body using Zod

Jasser Mark Arioste

Jasser Mark Arioste

How to Add Typescript Types to NextJS API Request Body using Zod

In this tutorial, you'll learn how to add typescript types to the NextJS API request body using the typescript schema declaration and validation library, Zod.

Introduction #

When we're using typescript in our apps, we really don't want to use the any type because this will remove any type-checking in our code and is easily error-prone. However, that's basically what the type NextApiRequest["body"] is. 

Using the zod library will allow us to create secure and robust NextJS APIs to make sure our request body is free from errors before processing them in the API handlers.

Examining NextApiRequest Type Definition #

To be able to extend NextApiRequest, I like to first examine the NextApiRequest type from Next's type definition files: utils.d.ts.

// node_modules/next/dist/shared/lib/utils.d.ts
/**
 * Next `API` route request
 */
export interface NextApiRequest extends IncomingMessage {
    /**
     * Object of `query` values from url
     */
    query: Partial<{
        [key: string]: string | string[];
    }>;
    /**
     * Object of `cookies` from header
     */
    cookies: Partial<{
        [key: string]: string;
    }>;
    body: any;
    env: Env;
    preview?: boolean;
    /**
     * Preview data set on the request, if any
     * */
    previewData?: PreviewData;
}
12345678910111213141516171819202122232425

As you can see on line 18, the "body" property is of type any.

How to Extend the NextApiRequest Manually #

And since NextApiRequest is an interface, we could extend it and override the body property by defining something like the one below on our API route:

// pages/api/hello.ts
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from "next";

// 👇 Define new request body
export interface HelloApiRequest extends NextApiRequest {
  // let's say our request accepts name and age property
  body: {
    name: string;
    age: number;
  };
}

export default function handler(
  /*req: NextApiRequest*/ req: HelloApiRequest,
  res: NextApiResponse
) {
  const { name, age } = req.body; // 👈 body is now fully typed
  if (req.method === "POST") {
    res.status(200).json({ message: `Hello, I'm ${name}, ${age} years old.` });
  } else {
    res.status(405).send("Method not allowed.");
  }
}

123456789101112131415161718192021222324

This is all fine, but relying on typescript alone does not make this code robust. This is great during compile-time, but what about runtime? We know that in software, we should not trust the user input without some type of validation. This is where zod comes in.

Using zod to define the schema #

First, let's install zod as a dependency:

yarn add zod

Next, let's modify the api/hello.ts file to use zod:

// pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { object, string, number, TypeOf } from "zod";
// 👇 Define the schema using zod
const helloSchema = object({
  name: string(),
  age: number(),
});
// 👇 Define new request body
export interface HelloApiRequest extends NextApiRequest {
  // use TypeOf to infer the properties from helloSchema
  body: TypeOf<typeof helloSchema>;
}
export default function handler(
  /*req: NextApiRequest*/ req: HelloApiRequest,
  res: NextApiResponse
) {
  const { name, age } = req.body; // 👈 body is now fully typed same as before.
  if (req.method === "POST") {
    // validate the body using safeParse() method
    const result = helloSchema.safeParse(req.body);
    if (!result.success) {
      res.status(400).send({
        message: result.error.message,
      });
    }
    res.status(200).json({ message: `Hello, I'm ${name}, ${age} years old.` });
  } else {
    res.status(405).send("Method not allowed.");
  }
}
12345678910111213141516171819202122232425262728293031

The difference between this and the previous one is that we can easily validate our request body using the schema. This is much better and we get the benefit of automatically generating the body type using the TypeOf utility type from zod.

That's it!

Conclusion #

We learned how to add types to a NextJS API request body by extending the NextApiRequest interface and using zod schema to automatically generate the body type. Zod is an amazing library and if you'd like to learn more about it, check out the documentation.

If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on Twitter.

Credits: Image by Giani Pralea from Pixabay 

Share this post!

Related Posts

Disclaimer

This content may contain links to products, software and services. Please assume all such links are affiliate links which may result in my earning commissions and fees.
As an Amazon Associate, I earn from qualifying purchases. This means that whenever you buy a product on Amazon from a link on our site, we receive a small percentage of its price at no extra cost to you. This helps us continue to provide valuable content and reviews to you. Thank you for your support!
Donate to ReactHustle