Home > Back-end >  digest-fetch and webpack - fetch is not a function
digest-fetch and webpack - fetch is not a function

Time:04-26

The Problem

TypeError: fetch is not a function
    at DigestClient.fetch (webpack-internal:///./node_modules/digest-fetch/digest-fetch-src.js:48:24)
    at User.create (webpack-internal:///./node_modules/mongodb-atlas-api-client/src/user.js:53:26)
    at Function.createOrgDBUser (webpack-internal:///./src/OrgUtils.ts:87:51)
    at Function.createOrg (webpack-internal:///./src/apis/OrgAPI.ts:373:39)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async APIResponse.processHandlerFunction (webpack-internal:///./node_modules/apilove/lib/APIResponse.ts:27:31)

That's the error I'm getting trying to use [email protected] to create indexes on a collection. I'm seeing this error locally and in aws-lambda.

atlasClient.user.create({...})

I use webpack to bundle my api so I think the issue is in how I'm bundling but in my research I haven't been able to come up with a solution.

digest-fetch, used by mongodb-atlas-api-client, uses node-fetch in absence of the native fetch function. However, I believe my webpack configuration coupled with the way digest-fetch imports the library is what's causing the issue.

The following line of code is from node-modules/digest-fetch/digest-fetch-src.js:8

if (typeof(fetch) !== 'function' && canRequire) var fetch = require('node-fetch')

If I change that to the below, everything works fine. In other words, it's importing the module not the main exported fetch function from node-fetch.

if (typeof(fetch) !== 'function' && canRequire) var fetch = require('node-fetch').default

The node-fetch/package.json describes three entry points.

"main": "lib/index.js",
"browser": "./browser.js",
"module": "lib/index.mjs",

I think what's happening is my webpack configuration is telling webpack to use the .mjs module entry point to build its output from node-fetch, which does export default fetch;.

My webpack.config.js

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CopyPlugin = require('copy-webpack-plugin')
const NodemonPlugin = require('nodemon-webpack-plugin')
const ZipPlugin = require('zip-webpack-plugin')

module.exports = (env, argv) => {
  const config = {
    target: 'node',
    watchOptions: {
      ignored: ['node_modules', '*.js', '*.js.map', '*.d.ts'],
    },
    entry: './src/APIHandler.ts',
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: `APIHandler.js`,
      libraryTarget: 'commonjs',
    },
    optimization: {
      minimize: false,
      // minimizer: [new TerserPlugin({})],
    },
    resolve: {
      extensions: ['.ts', '.json', '.mjs', '.js'],
    },
    module: {
      rules: [
        {
          test: /\.tsx?$/,
          use: {
            loader: 'ts-loader',
            options: {
              transpileOnly: true,
              allowTsInNodeModules: true,
            },
          },
        },
      ],
    },
    plugins: [
      new CleanWebpackPlugin(),
      new CopyPlugin([
        { from: path.join(__dirname, 'src/certs'), to: path.resolve(__dirname, 'dist', 'certs') },
      ]),
      new NodemonPlugin(),
    ],
  }

  // eslint-disable-next-line no-console
  console.log({ mode: argv.mode })

  if (argv.mode === 'development') {
    config.devtool = 'eval-cheap-module-source-map'
  }

  if (argv.mode === 'production') {
    config.plugins.push(
      new ZipPlugin({
        filename: 'handler',
      })
    )
  }

  return config
}

FWIW, here's the version of each library currently installed in my node_modules. I'm using node v12.22.7 locally.

"digest-fetch": "1.2.1",
"mongodb-atlas-api-client": "2.31.0",
"node-fetch": "2.6.7",
"webpack": "4.46.0"

The Question

What am I missing? What change do I need to make to my webpack.config.js to have the require properly resolve to the main module export from node-fetch?

NOTE: In my research I've found other people having this problem but no resolutions that have helped me.

CodePudding user response:

The solution was pretty simple. I added mainFields to the webpack config's resolve property.

resolve: {
  extensions: ['.ts', '.json', '.mjs', '.js'],
  mainFields: ['main', 'module'],
}

This tells webpack to first use the main property of a module's package.json, then fallback to the module property if it's not found.

For more information, see webpack's documentation.

  • Related