0

I want to write a wrapper for my NextJs API routes that checks for a JWT in the request, validates it and then executes the original API handler.

So I've defined my wrapper function like so

interface ApiError {
  message: string,
}

export async function withJwt<T>(
  handler: ((req: NextApiRequest, res: NextApiResponse<T>, user?: User)=>void|Promise<void>))
  : Promise<(req: NextApiRequest, res: NextApiResponse<T | ApiError>)=>Promise<void>> {  
  return async (req: NextApiRequest, res: NextApiResponse<T | ApiError>) => {
    const authHeader = req.headers.authorization;
    if(!authHeader || !authHeader.startsWith(JWT_PREFIX)){ 
      return res.status(401).json({
        message: `Provide the header 'Authorization: ${JWT_PREFIX}<token>'`,
      });
    }
    let user: User;
    try {
      user = await verify(authHeader.substring(JWT_PREFIX.length));
    } catch(err) {
      return res.status(401).json({message: (err as any).message as string});
    }

    try {
      return handler(req, res, user);
    } catch(err){
      return res.status(500).json({message: (err as any).message});
    }
  };
}

Then, my API route looks like this

// page/api/hello.ts

type Data = {
  name: string
}

const wrapped = withJwt((
  req: NextApiRequest,
  res: NextApiResponse<Data>
) => {
  res.status(200).json({ name: 'John Doe' })
});

export default wrapped;

Yet, when I navigate to /api/hello, I get the following error output:

Server Error

TypeError: resolver is not a function

Uncaught at Object.apiResolver (file:///root/projects/taskmanager-next/node_modules/next/dist/server/api-utils.js:101:15) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async DevServer.runApi (file:///root/projects/taskmanager-next/node_modules/next/dist/server/next-server.js:320:9) at async Object.fn (file:///root/projects/taskmanager-next/node_modules/next/dist/server/base-server.js:486:37) at async Router.execute (file:///root/projects/taskmanager-next/node_modules/next/dist/server/router.js:228:32) at async DevServer.run (file:///root/projects/taskmanager-next/node_modules/next/dist/server/base-server.js:598:29) at async DevServer.run (file:///root/projects/taskmanager-next/node_modules/next/dist/server/dev/next-dev-server.js:444:20) at async DevServer.handleRequest (file:///root/projects/taskmanager-next/node_modules/next/dist/server/base-server.js:305:20)

What am I missing here?

1 Answer 1

1

Okay I figured it out:

My mistake was to make withJwt an async function. Only the returned function needs to be async and therefore return a Promise.

So the following is working:

export function withJwt<T>(
  handler: ((req: NextApiRequest, res: NextApiResponse<T>, user?: User)=>void|Promise<void>))
  : (req: NextApiRequest, res: NextApiResponse<T | ApiError>)=>Promise<void> {  
  return async (req: NextApiRequest, res: NextApiResponse<T | ApiError>) => {
    const authHeader = req.headers.authorization;
    if(!authHeader || !authHeader.startsWith(JWT_PREFIX)){ 
      return res.status(401).json({
        message: `Provide the header 'Authorization: ${JWT_PREFIX}<token>'`,
      });
    }
    let user: User;
    try {
      user = await verify(authHeader.substring(JWT_PREFIX.length));
    } catch(err) {
      return res.status(401).json({message: (err as any).message as string});
    }

    try {
      return handler(req, res, user);
    } catch(err){
      return res.status(500).json({message: (err as any).message});
    }
  };
}
Sign up to request clarification or add additional context in comments.

1 Comment

Note: async function always returns a promise; so calling withJwt(...) assigned a promise to wrapped, not the inner function returned by withJwt.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.