Handy Body and Error Parsing Utilities for Next.js

A couple of useful functions for parsing and validating data in Next.js API routes.

Published on Nov 30, 20224 min read

When dealing with requests and errors in Next.js API routes, we want to make sure that we are handling them in a way that is consistent and predictable. Unfortunately, we're not always given great typings for free and sometimes we have to do a little bit of work to make sure that we're parsing (or at least massaging) the data into something useful.

Parsing the Body of a NextApiRequest

The Next.js API route handler does not provide any built-in utilities for parsing the body of a request or validating the data contained in it, and fair enough as it's a total crapshoot (specifically, the NextApiRequest type defines the body property as any).

The common solution is simply to cast the body property to a specific type, which is essentially what we'll be doing but using a Typescript "generic" - a type that can be used in place of another type.

src/parseBody.tsx
import type { NextApiRequest } from 'next';
 
export const parseBody = <T,>(req: NextApiRequest): T => {
  if (typeof req.body === 'string') {
    return JSON.parse(req.body) as T;
  }
 
  return req.body as T;
};

This code defines a function called parseBody that takes in a NextApiRequest object and returns a value of generic type T. This function is used to parse the body of an API request in a Next.js application. The NextApiRequest object is passed to API routes in Next.js and contains information about the request, such as the request body, headers, and query parameters.

The parseBody function checks if the req.body property is a string, and if it is, it uses the JSON.parse method to parse it into a JavaScript object. It then returns the parsed object as a value of type T. If req.body is not a string, it is assumed to be a JavaScript object already and is returned directly as a value of type T.

While not completely bulletproof (and assuming that you definitely want JSON), it is a handy utility function that can be used to parse the body of an API request in a Next.js application and convert it into a JavaScript object that can be used in the route handler, like so:

import { parseBody } from './parseBody';
import type { NextApiHandler } from 'next';
 
const handler: NextApiHandler = (req, res) => {
  const body = parseBody<{ email?: string }>(req);
 
  if (!body.email) {
    res.status(400).json({ error: 'Email is required' });
    return;
  }
 
  // do something with `body.email`
};
 
export default handler;

Parsing an Error

When dealing with error handling in Typescript, the catch clause variable type annotation must be 'any' or 'unknown' if specified. This is because the type of the error is not known in advance, and it may be an instance of the built-in Error class or any other type of error.

In most cases, we want to be able to handle any type of error that is thrown, and we want to be able to parse it into a string that can be used in the response. While we could simply cast the error to a string, this is not a great solution because it will result in a runtime error if the error is not a string.

Additionally, most errors thrown are of type Error, which has a message property that contains a string representation of the error. We can use this property to parse the error into a string that can be used in the response.

src/parseError.tsx
export const parseError = (error: unknown): string => {
  if (error instanceof Error) {
    return error.message;
  }
 
  return `${error as string}`;
};

This code defines a function called parseError that takes in a variable error of the type unknown. In the body of the parseError function, the code checks if the error variable is an instance of the built-in Error class. If it is, the function returns the message property of the error object, which will contain a string representation of the error message.

If the error variable is not an instance of the Error class, the code uses type casting to treat it as a string and return it as a string. This allows the function to handle any value that is passed to it, regardless of its type.

This is handy for ensuring that the error message is always a string, which is useful for sending it in the response.

Anyway, that's it for this one. I hope you found it useful. If you have any questions or comments, feel free to let me know on Twitter. Thanks for reading!


Back to Blog