vercel / next.js

The React Framework
https://nextjs.org
MIT License
126.98k stars 26.98k forks source link

Using isomorphic modules in middlewares #36974

Closed brc-dd closed 2 years ago

brc-dd commented 2 years ago

Verify canary release

Provide environment information

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 10 Pro
Binaries:
  Node: 16.15.0
  npm: N/A
  Yarn: N/A
  pnpm: N/A
Relevant packages:
  next: 12.1.7-canary.6
  react: 18.1.0
  react-dom: 18.1.0

What browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

Describe the Bug

Put something like this in pages/_middleware.js:

import { NextResponse } from 'next/server'
import fetch from 'isomorphic-fetch'

export const middleware = async (req, ev) => NextResponse.next()

It throws error like:

Module not found: Can't resolve 'querystring'

Import trace for requested module:
./node_modules/.pnpm/node-fetch@2.6.7/node_modules/node-fetch/lib/index.mjs
./node_modules/.pnpm/isomorphic-fetch@3.0.0/node_modules/isomorphic-fetch/fetch-npm-node.js
./pages/_middleware.js

https://nextjs.org/docs/messages/module-not-found

You're using a Node.js module (querystring) which is not supported in the Edge Runtime.
Learn more: https://nextjs.org/docs/api-reference/edge-runtime

I know that we have access to fetch inside middlewares, but the question I want to ask is how can one write a module that works on both Node.js and edge-runtimes without making the users to manually specify the file to import?

Expected Behavior

I expect Next.js to by default import browser field of package.json inside middlewares. Or don't check for native modules inside conditions that are not true on middlewares. For example in this one:

import { NextResponse } from 'next/server'

let crypto

if (process?.versions?.node) {
  console.log('is Node')
  // uncommenting below line throws error: You're using a Node.js module (crypto) which is not supported in the Edge Runtime.
  // crypto = require('crypto').webcrypto // or maybe @peculiar/webcrypto
} else {
  console.log('not Node') // this gets printed on terminal
  crypto = global?.crypto || window?.crypto // and some other fallbacks
}

export const middleware = async (req, ev) => NextResponse.next()

EDIT:

I understand the reasoning behind not importing browser field as mentioned at https://github.com/vercel/next.js/issues/36844#issuecomment-1124358786. But for the second one (selective requiring/import), I think that's a bug.

brc-dd commented 2 years ago

I ended up doing:

const nodeVer = typeof process !== 'undefined' && process.versions?.node
const nodeRequire = nodeVer
  ? typeof __webpack_require__ === 'function'
    ? __non_webpack_require__
    : require
  : undefined
github-actions[bot] commented 2 years ago

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.