How to Add Typescript Types to NextJS API Request Body using Zod
Jasser Mark Arioste
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;
}
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.");
}
}
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.");
}
}
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


