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);
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." });
}
};