Search code examples
javascripttypescriptauthenticationnext.jsmiddleware

How can I add custom type to NextApiRequest in Next.js?


I have a middleware that checks whether the user making the request has a valid JWT token for requests to the APIs in Next.js. I wrote this middleware using TypeScript, but I get a type error.

My code is below:

import { verifyJwtToken } from "../utils/verifyJwtToken.js";
import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
import cookie from "cookie"

const authMiddleware = (handler: NextApiHandler) => async (req: NextApiRequest, res: NextApiResponse) => {
  try {
    
 
    const cookies = cookie.parse(req.headers.cookie || '')
    const token = cookies.token
    const verifiedToken = await verifyJwtToken(token)

    if (!verifiedToken) {
      throw new Error('You have no access to do it.')
    }
    
    const userId = Number(verifiedToken.id)

    // Call the API route handler with the updated request object
    return handler({ ...req, userId }, res)
  } catch (err) {
    console.error(err)
    res.status(401).json({ message: 'You have no access to do it.' })
  }
}

export default authMiddleware;

The error is below:

Argument of type '{ userId: number; query: Partial<{ [key: string]: string | string[]; }>; cookies: Partial<{ [key: string]: string; }>; body: any; env: Env; preview?: boolean | undefined; ... 35 more ...; eventNames(): (string | symbol)[]; }' is not assignable to parameter of type 'NextApiRequest'.

Object literal may only specify known properties, and 'userId' does not exist in type 'NextApiRequest'.ts(2345)

To fix this error, I created a custom interface that extends NextApiRequest and includes the userId property. Here's an example:

import { NextApiRequest } from 'next'

interface CustomNextApiRequest extends NextApiRequest {
  userId: number
}

export default CustomNextApiRequest;

Then, I tried to use this custom interface as the type for your req parameter in the middleware function, like this:

import CustomNextApiRequest from './CustomNextApiRequest';

const authMiddleware = (handler: NextApiHandler) => async (req: CustomNextApiRequest, res: NextApiResponse) => {
  // ...
}

But didn't work. Btw, this code works. But I want to fix this type error.

And by the way, here is an example of how I use the authMiddleware:

async function addCommentHandler(req: NextApiRequest, res: NextApiResponse) {
  // My code goes here...
}
export default authMiddleware(addCommentHandler);

Solution

  • Your CustomNextApiRequest declaration is correct. The issue is in how your middlewares are chained together. I would suggest you have a CustomNextApiHandler, as below (feel free to adapt the return type):

    interface CustomNextApiRequest extends NextApiRequest {
      userId: number;
    }
    export type CustomNextApiHandler = (req: CustomNextApiRequest, res: NextApiResponse) => void;
    

    Your addCommentHandler would use the above CustomNextApiHandler type:

    export const addCommentHandler: CustomNextApiHandler = (req, res) => {};
    

    And finally, your authMiddleware will be as follows (notice the new comment):

    const authMiddleware =
      (handler: CustomNextApiHandler) => async (req: CustomNextApiRequest, res: NextApiResponse) => {
        try {
          const cookies = cookie.parse(req.headers.cookie || "");
          const token = cookies.token;
          const verifiedToken = await verifyJwtToken(token);
    
          if (!verifiedToken) {
            throw new Error("You have no access to do it.");
          }
          const userId = Number(verifiedToken.id);
          // Added it like this instead of destructuring it
          req.userId = userId;
          // Call the API route handler with the updated request object
          return handler(req, res);
        } catch (err) {
          console.error(err);
          res.status(401).json({ message: "You have no access to do it." });
        }
      };