hoangvvo / next-connect

The TypeScript-ready, minimal router and middleware layer for Next.js, Micro, Vercel, or Node.js http/http2

Home Page:https://www.npmjs.com/package/next-connect

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cannot access '__WEBPACK_DEFAULT_EXPORT__' before initialization

brianlovin opened this issue · comments

Hey team! I'm working through adding next-connect to all of my API pages in a Next.js project. I'm using the factory pattern that I've seen outlined in other issues:

import nc from 'next-connect';

export default function nextConnectHandler() {
  return nc<NextApiRequest, NextApiResponse>({
    onError(error: Error, req: NextApiRequest, res: NextApiResponse) {
      console.log({ error });
      res.status(501).json({ error: `${error.message}` });
    },
    onNoMatch(req: NextApiRequest, res: NextApiResponse) {
      res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
    },
  });
}

Then, from an API route, I can call it like this:

// /api/foo.ts
import nextConnectHandler from 'utils/nextConnectHandler';

const apiRoute = nextConnectHandler();

apiRoute.get(async (req, res) => {
  // some logic
});

export default apiRoute;

On the client, results are sporadic. Sometimes my API requests resolve just fine, but other times I'm getting waves of this error:

Screen Shot 2022-05-16 at 22 37 20@2x

error - (api)/utils/nextConnectHandler.ts (23:44) @ nextConnectHandler
TypeError: (0 , next_connect__WEBPACK_IMPORTED_MODULE_1__.default) is not a function

Have you encountered something like this before? Googling and issue search on this repo didn't turn up anything quite like it.

I have also tried:

  • rm -rf node_modules yarn.lock && yarn to make sure I'm using latest package
  • delete .next directory and restart the app with vercel dev to recompile

Interestingly, I can get some of my pages to load and fetch data (correctly!) when server rendered. But as soon as the page renders and the React hydrates, I have some swr calls to my API that are failing with the above error.

Here are my versions:

"next-connect": "^0.12.2",
"next": "^12.1.6",
commented

So this is the CJS export of next-connect: https://unpkg.com/next-connect@0.12.2/dist/index.cjs

This is the ESM version: https://unpkg.com/next-connect@0.12.2/dist/index.js

It seems that webpack is trying to pull the CJS version which has the export written as:

module.exports = function

(So no .default - which is technically inaccurate according to ESM spec). When we write import nc from "next-connect" and transpile that, it would become const nc = require("next-connect").default; which undefined considering the module.exports above.

Normally, you would enable esModuleInterop so that TS will transform the import code (import nc from "next-connect") to something like:

__importDefault(require("next-connect")); // automatically handle the discrepency

and it would work.

However, if it actually pulls the ESM version, it should not be a problem, so I am not sure why this is not the case.

I will look into this asap.

commented

Hey @brianlovin, could you please give me some more info on your usage?

  • where do you use it? In API Routes, in GetServerSideProps? Could you post a snippet of where you call that factory function.
  • Your Next.js version, TSConfig, Webpack Config (if applicable)

I definitely saw the issue above (not in next-connect but other packages with default export and in CommonJS) so my guess in the previous comment could be the case.

However, I just spin up a Next.js project but could not reproduce it. (I believe it's valid though, just want a way to reproduce to come up w a solution)

Thanks!

Thanks for investigating this @hoangvvo

I was using this at the root level of each of my API pages. Here's loosely what I did:

1. I started with a nextConnect instance per file:

import type { NextApiRequest, NextApiResponse } from 'next';
import Cors from 'cors';
import { prisma } from '~/web/lib/prisma';
import nextConnect from 'next-connect';
import { User } from '@prisma/client';
import { getToken } from 'next-auth/jwt';

async function getUser(email: string): Promise<User | null> {
  return await prisma.user.findUnique({
    where: { email },
  });
}

const apiRoute = nextConnect<NextApiRequest, NextApiResponse>({
  onError(error, req, res) {
    res.status(501).json({ error: `Error while validating Figma token: ${error.message}` });
  },
  onNoMatch(req, res) {
    res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
  },
});

apiRoute.use(Cors());

apiRoute.get(async (req, res) => {
  const token = await getToken({ req });
  const data = await getUser(token.email)
  return res.status(200).json({ status: 'ok', data });
});

export default apiRoute;

2. I abstracted out the apiRoute constructor to a new file

import nextConnect from 'next-connect'

export default function createApiRoute() {
  return nextConnect<NextApiRequest, NextApiResponse>({
    onError(error, req, res) {
      res.status(501).json({ error: `Error while validating Figma token: ${error.message}` });
    },
    onNoMatch(req, res) {
      res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
    },
   });
}

3. Then I imported that factory for apiRoutes into each of my API pages.

// ...
import nc from 'helpers/createNextConnect'

async function getUser(email: string): Promise<User | null> {
  return await prisma.user.findUnique({
    where: { email },
  });
}

const apiRoute = nc()

apiRoute.use(Cors());

apiRoute.get(async (req, res) => {
  const token = await getToken({ req });
  const data = await getUser(token.email)
  return res.status(200).json({ status: 'ok', data });
});

export default apiRoute;

That's where the errors above started happening. Hope this makes sense.

commented

Thanks for investigating this @hoangvvo

I was using this at the root level of each of my API pages. Here's loosely what I did:

1. I started with a nextConnect instance per file:

import type { NextApiRequest, NextApiResponse } from 'next';
import Cors from 'cors';
import { prisma } from '~/web/lib/prisma';
import nextConnect from 'next-connect';
import { User } from '@prisma/client';
import { getToken } from 'next-auth/jwt';

async function getUser(email: string): Promise<User | null> {
  return await prisma.user.findUnique({
    where: { email },
  });
}

const apiRoute = nextConnect<NextApiRequest, NextApiResponse>({
  onError(error, req, res) {
    res.status(501).json({ error: `Error while validating Figma token: ${error.message}` });
  },
  onNoMatch(req, res) {
    res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
  },
});

apiRoute.use(Cors());

apiRoute.get(async (req, res) => {
  const token = await getToken({ req });
  const data = await getUser(token.email)
  return res.status(200).json({ status: 'ok', data });
});

export default apiRoute;

2. I abstracted out the apiRoute constructor to a new file

import nextConnect from 'next-connect'

export default function createApiRoute() {
  return nextConnect<NextApiRequest, NextApiResponse>({
    onError(error, req, res) {
      res.status(501).json({ error: `Error while validating Figma token: ${error.message}` });
    },
    onNoMatch(req, res) {
      res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
    },
   });
}

3. Then I imported that factory for apiRoutes into each of my API pages.

That's where the errors above started happening. Hope this makes sense.

Thanks @brianlovin! Could you also include your TypeScript Config? I feel like it might have something to do with esModuleInterop option. Also with your next version too would be great! (just saw it in your first issue)

Sure! This is mostly the Next.js default:

{
	"compilerOptions": {
		"target": "es5",
		"lib": ["dom", "dom.iterable", "esnext"],
		"allowJs": true,
		"skipLibCheck": true,
		"strict": true,
		"forceConsistentCasingInFileNames": true,
		"noEmit": true,
		"esModuleInterop": true,
		"module": "esnext",
		"moduleResolution": "node",
		"resolveJsonModule": true,
		"isolatedModules": true,
		"jsx": "preserve",
		"incremental": true,
		"baseUrl": ".",
		"paths": {
			"~/*": ["../*"]
		}
	},
	"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "prisma/seed.js", "prisma/seedData.js"],
	"exclude": ["node_modules"]
}