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.62k stars 65 forks source link

next-connect doesn't work in production environment #149

Closed yuvaraj-anbarasan closed 2 years ago

yuvaraj-anbarasan commented 3 years ago

The api routes with next-connect handler works as expected in dev environment but doesn't work in prod environement.

route handler

import NextConnect from "next-connect";

export default NextConnect().use((req, res, next) => {
    if (!['GET', 'POST'].includes(req.method))
        res.status(405).json({...});
    else
        next();
});

api route /api/user/detail

export default Handler.get((req, res) => {
  ...
});

api route /api/user/create

export default Handler.post((req, res) => {
  ...
});

Every api call gets response from the first api which was called. For example if I call api/user/create at first, all other api's called after this gets response from api/user/create handler. And this happens only in production environment.

hoangvvo commented 2 years ago

This is because same Handler is being reused. You should create new instance of nc in every route.

yuvaraj-anbarasan commented 2 years ago

But it worked without any issues in dev environment.

hoangvvo commented 2 years ago

Yes because for some reason in production the .post and .get gets set into the same Handler instance that you are exported from route handler:

So it looks some thing like this.

// intially
Handler
  - use ....

// when user hits `api/user/create`, the `get` handler get added to the same instance so it becomes
Handler
  - use ....
  - post ....

// when user hits `/api/user/detail`, the `get` handler get added to the same instance so it becomes
Handler
  - use ....
  - post ....
  - get ...

Now this is not correct inside /create we don't want the get handler and in /detail' we don't want topost` handler. However this happens because you use the same instance.

It is really the same of mutating the same object. So the solution is not to import the same nc() instance but to rewrite it as a factory like so:

import NextConnect from "next-connect";

export default function factory() {
 return  NextConnect().use((req, res, next) => {
    if (!['GET', 'POST'].includes(req.method))
        res.status(405).json({...});
    else
        next();
});
}

Then, in api route /api/user/detail

export default factory().get((req, res) => {
  ...
});

and in api route /api/user/create:

export default factory().post((req, res) => {
  ...
});

Now it works because it does not use the same nc instance anymore.

hoangvvo commented 2 years ago

Perhaps the reason it works in development is because hot reload "reset" the JS so it works as if nothing has been added to the reused Handler.

hoangvvo commented 2 years ago

I added a new section to address this error: https://github.com/hoangvvo/next-connect#common-errors. Let me know if it resolves your issue.