hoangvvo / next-connect

The TypeScript-ready, minimal router and middleware layer for Next.js, Micro, Vercel, or Node.js http/http2
https://www.npmjs.com/package/next-connect
MIT License
1.64k stars 65 forks source link

NextJS API route with multiple middlewares #150

Closed mikeharrison4 closed 2 years ago

mikeharrison4 commented 3 years ago

Hi,

I am relatively new to NextJS and next-connect. I have middleware setup which verifies authorization, see below

 const getAuthHandler = () => {
  return handler({
    onError(err, req, res) {
      res.status(403).json({ error: err.toString() });
    }
  }).use(async (req, res, next) => {
    const idToken = getToken(req);
    try {
      req.user = await verifyIdToken(idToken);
      const userFromDb = await db
        .collection('users')
        .where('userId', '==', req.user.uid)
        .limit(1)
        .get();

      req.user.handle = userFromDb.docs[0].data().handle;
      return next();
    } catch (err) {
      console.log(`Error while verifying token ${err}`);
      return res.status(403).json({ error: err });
    }
  });
};

export default getAuthHandler;

I am using this in all of my API routes that I want to protect with authorization, like below:

export default getAuthHandler()
  .post(async (req, res) => {
     ...code here
  }

I have run into an issue with an API route where I have a get request which I do not want to be protected.. but I then have a post request on the same route which I do want to be protected. I am unsure how to achieve this with my current flow. See below for the API route:

const handler = nc();
export default handler
  .get(async (req, res) => {
     (i do not want to protect this)
  })
  .post(async (req, res) => {
    (i do want to protect this)
  });

How do I add middleware for just the post request in this scenario?

Would appreciate any help on this, thanks in advance.

TERADA-DANTE commented 3 years ago

@mikeharrison4 Good day. How about checking req.method in your middleware?

hoangvvo commented 3 years ago

One way to do that is to use the middleware per method (instead of on .use()):

const protected = async (req, res, next) => {
    const idToken = getToken(req);
    try {
      req.user = await verifyIdToken(idToken);
      const userFromDb = await db
        .collection('users')
        .where('userId', '==', req.user.uid)
        .limit(1)
        .get();

      req.user.handle = userFromDb.docs[0].data().handle;
      return next();
    } catch (err) {
      console.log(`Error while verifying token ${err}`);
      return res.status(403).json({ error: err });
    }
  }

const handler = nc();
export default handler
  .get(async (req, res) => {
     (i do not want to protect this)
  })
  .post(protected, async (req, res) => {
    (i do want to protect this)
  });  
mikeharrison4 commented 3 years ago

One way to do that is to use the middleware per method (instead of on .use()):

const protected = async (req, res, next) => {
    const idToken = getToken(req);
    try {
      req.user = await verifyIdToken(idToken);
      const userFromDb = await db
        .collection('users')
        .where('userId', '==', req.user.uid)
        .limit(1)
        .get();

      req.user.handle = userFromDb.docs[0].data().handle;
      return next();
    } catch (err) {
      console.log(`Error while verifying token ${err}`);
      return res.status(403).json({ error: err });
    }
  }

const handler = nc();
export default handler
  .get(async (req, res) => {
     (i do not want to protect this)
  })
  .post(protected, async (req, res) => {
    (i do want to protect this)
  });  

This worked great, thankyou!