Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AbortController issue #204

Closed
akarale opened this issue Aug 5, 2020 · 31 comments
Closed

AbortController issue #204

akarale opened this issue Aug 5, 2020 · 31 comments

Comments

@akarale
Copy link

akarale commented Aug 5, 2020

TypeError: AbortController is not a constructor

Anyone else getting this error? How did you resolve it?

@jugglinmike
Copy link
Contributor

Not me! But I might be able to help with more information.

  • Which version of airtable.js are you using?
  • What is the complete error stack?
  • In what environment are you observing the error? If Node.js, the version of Node.js may help. If a web browser, the name and version of the browser may help.
  • Can you provide a minimal test case?

@Aias
Copy link

Aias commented Aug 8, 2020

I've also been getting this error after trying to upgrade to airtable 0.9.0 Glad I'm not the only one.

For a long time now I've been using airtable.js in my Netlify lambda functions, but upgrading to 0.9.0 has broken it without a resolution that I can find. If I go back to 0.8.1 it works fine, so there seems to be something going on with the recent move to node-fetch over request.

The Node version is 10.21.0.

Here's the full stack, though I'm not sure it's particularly helpful:

Lambda server is listening on 9000
Request from ::1: GET /.netlify/functions/airtableGet?base=commonplace&table=extracts
Response with status 500 in 57 ms.
Error during invocation:  TypeError: AbortController is not a constructor
    at runAction (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:9585:22)
    at Base.runAction (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:4780:5)
    at inner (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:8111:27)
    at Query.eachPage (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:8134:5)
    at Query.eachPage (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:1665:16)
    at Object.exports.handler (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:10666:6)
    at /Users/tricknombley/Code/netlify-api/node_modules/netlify-lambda/lib/serve.js:146:27
    at Layer.handle [as handle_request] (/Users/tricknombley/Code/netlify-api/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/tricknombley/Code/netlify-api/node_modules/express/lib/router/route.js:137:13)
    at next (/Users/tricknombley/Code/netlify-api/node_modules/express/lib/router/route.js:131:14)

A minimal test case would probably involve setting up a super basic Netlify lambda environment with the following single function:

var Airtable = require('airtable');
var base = new Airtable({ apiKey: '<my key>' }).base('<my base>');

exports.handler = async function (event, context) {
    base('<table name>')
        .select({
            // Selecting the first 3 records in Grid view:
            maxRecords: 3,
            view: 'Grid view'
        })
        .eachPage(
            function page(records, fetchNextPage) {
                // This function (`page`) will get called for each page of records.

                records.forEach(function (record) {
                    console.log('Retrieved', record.get('title'));
                });

                // To fetch the next page of records, call `fetchNextPage`.
                // If there are more records, `page` will get called again.
                // If there are no more records, `done` will get called.
                fetchNextPage();
            },
            function done(err) {
                if (err) {
                    console.error(err);
                    return;
                }
            }
        );
};

@reelmatt
Copy link

Given the error name, it looks like this points to line 46 in lib/base.js:

var controller = new AbortController();

While I haven't gotten a working solution yet, playing around with it a bit is seems like the issue is related to the switch from request to node-fetch as @Aias mentioned and how the library deals with Polyfills (the AbortController interface is experimental, per MDN).

I also opened #205 which could be pointing to the same/similar issue, but could also be Jest-specific instead of a problem related to Airtable.js.

@jugglinmike
Copy link
Contributor

@Aias Version 0.9.0 works for me using Netlify's CLI for local testing and Node.js version 10.21.0. There may be subtle differences in the production environment, but there are other causes that could explain this. For starters: is global.window defined in your lambda function?

@jugglinmike
Copy link
Contributor

@reelmatt Jest users should be able to upgrade to version 0.9.0 by installing the whatwg-fetch module and including that in their test environment via the --setupFiles option.

Here's a quick demo:

$ ./node_modules/.bin/jest
 FAIL  ./index.test.js
  ● Test suite failed to run

    ReferenceError: Request is not defined

    > 1 | const Airtable = require('airtable');
        |                  ^
      2 |
      3 | test('my test', async () => {
      4 |       Airtable.configure({

      at Object.<anonymous> (node_modules/airtable/lib/abort-controller.js:5:25)
      at Object.<anonymous> (node_modules/airtable/lib/base.js:8:23)
      at Object.<anonymous> (node_modules/airtable/lib/airtable.js:3:12)
      at Object.<anonymous> (index.test.js:1:18)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.849 s, estimated 1 s
Ran all test suites.

$ npm install --save-dev whatwg-fetch
$ ./node_modules/.bin/jest --setupFiles whatwg-fetch
 PASS  ./index.test.js
  ✓ my test (363 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.379 s
Ran all test suites.

This may be the long-term solution because Jest uses JSDom, and JSDom is not supported by airtable.js.

@Aias
Copy link

Aias commented Aug 10, 2020

Hey @jugglinmikewindow.global is not defined when I use Netlify's local cli. I'm surprised that it would be for you without additional setup, since these are supposed to be server-side functions.

@jugglinmike
Copy link
Contributor

@Aias It's global.window that I'm wondering about, but not because it's defined in my environment. If some other code in the lambda environment (maybe via another dependency) defined that global variable, then it could interfere with the logic that airtable.js 0.9.0 uses to identify which environment it is running in.

Looking deeper, I'm also curious about how airtable.js is being imported. The minimal test case uses Node.js's require, but that doesn't square with the error stack trace. Each frame references a file called airtableGet.js, but this library doesn't define such a file. The function names there do seem to align with the internals of this library, though. I first thought that Netlify's CLI was running a deployment-time bundling process, but the error stack seems to describe files on your development machine.

Do you know where airtableGet.js is coming from?

@Aias
Copy link

Aias commented Aug 10, 2020

@jugglinmike Ah sorry there's some confusion here, that's my fault.

On the window.global vs. global.window thing, I think I just miswrote the order before - in any case it doesn't matter, it's undefined either way.

airtableGet is the name of my lambda function, so it's what gets invoked when I send a request to http://localhost:8888/.netlify/functions/airtableGet/. Here's the stack trace which is just setting up a netlify dev environment with exactly that code from above, inside a file called airtableGet.js:

Error during invocation:  TypeError: AbortController is not a constructor
    at runAction (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:9585:22)
    at Base.runAction (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:4780:5)
    at inner (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:8111:27)
    at Query.eachPage (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:8134:5)
    at Query.eachPage (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:1665:16)
    at Object.exports.handler (/Users/tricknombley/Code/netlify-api/lambda/airtableGet.js:10666:6)
    at /Users/tricknombley/Code/netlify-api/node_modules/netlify-lambda/lib/serve.js:146:27
    at Layer.handle [as handle_request] (/Users/tricknombley/Code/netlify-api/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/tricknombley/Code/netlify-api/node_modules/express/lib/router/route.js:137:13)
    at next (/Users/tricknombley/Code/netlify-api/node_modules/express/lib/router/route.js:131:14)

@Aias
Copy link

Aias commented Aug 10, 2020

If you have a repository that works with airtable 0.9.0 and that minimal netlify test case, I'd be happy to clone it and see if it runs on my machine.

@jugglinmike
Copy link
Contributor

Sure. Here's a repository using that same code:

https://github.com/bocoup/airtablejs-netlify-demo

When I change the values according to my account and Airtable Base schema, the lambda function development environment prints data from the Base as anticipated.

That may be a dead end, though, because the error stack trace above indicates that airtableGet.js is substantially different from the script we've been discussing. It references a number of functions that are not defined in that script. In addition, while the script is just 33 lines long, the frames in the stack trace reference line numbers in the thousands.

@Aias
Copy link

Aias commented Aug 10, 2020

Thanks for all your help. Your example repo works. I was using the outdated netlify-lambda repository which does some kind of webpack compilation as part of its build step (I don't really understand the specifics). But when I switched everything from ES6 to CommonJS in my local repo, things started working again.

So I guess the moral is: drop dependency on netlify-lambda, use CommonJS module syntax?

@jugglinmike
Copy link
Contributor

Sounds like a plan to me!

That build process appears to be modifying the environment, so it's effectively a new execution environment--just slightly different than the environments which Airtable explicitly supports. It doesn't seem feasible for this project's maintainers to ensure proper integration there (especially if it's considered outdated).

So while I'm receptive to evidence in favor of reverting the latest release, your particular problem doesn't seem to qualify as a regression. We still don't know much about @akarale's situation, though...

studds added a commit to studds/airtable.js that referenced this issue Aug 26, 2020
@studds
Copy link

studds commented Aug 26, 2020

I'm getting this error as well. I'm using airtable via node and webpack. There seems to be an issue with the node imports:

var fetch = require('node-fetch');

and

module.exports = require('abort-controller');

In each case, the require refers the the module, rather than the function / class respectively.

@itay747
Copy link

itay747 commented Aug 27, 2020

Temporary workaround:

npm install webpack-node-externals --save-dev

Create webpack.functions.js in your project root like so:

// webpack.functions.js
const nodeExternals = require('webpack-node-externals');
module.exports = {
    externals: [nodeExternals()],
};

and modify your build script in package.json to be something like

"build:lambda": "netlify-lambda build lambda --config ./webpack.functions.js"

@matmeylan
Copy link

matmeylan commented Sep 9, 2020

We're also experiencing the same error (TypeError: AbortController is not a constructor) when using base.select().all() in our node.js app bundled with webpack.

Here is more information:

Environment:

  • Node 12.18.2
  • Airtable.js 0.9.0 (It does not happen with 0.8.1 ! I'm guessing node-fetch introduction)
  • Webpack 4.44.1

global.window is not defined at runtime, as it's a pure node app.

Stacktrace:

caught err TypeError: AbortController is not a constructor
    at runAction (webpack-internal:///./node_modules/airtable/lib/run_action.js:38:22)
    at Base.runAction (webpack-internal:///./node_modules/airtable/lib/base.js:111:5)
    at inner (webpack-internal:///./node_modules/airtable/lib/query.js:77:27)
    at Query.eachPage (webpack-internal:///./node_modules/airtable/lib/query.js:100:5)
    at Query.eval [as eachPage] (webpack-internal:///./node_modules/airtable/lib/callback_to_promise.js:19:16)
    at Query.all (webpack-internal:///./node_modules/airtable/lib/query.js:112:10)
    at eval (webpack-internal:///./node_modules/airtable/lib/callback_to_promise.js:38:20)
    at new Promise (<anonymous>)

After investigating more, the error occurs in run_action.js:38:

// AbortController in my bundled node app is the following:
/*
Object [Module] {
  AbortController: [Getter],
  AbortSignal: [Getter],
  default: [Function: AbortController]
}
*/
var controller = new AbortController(); // throws AbortController is not a constructor

Workaround:

Downgrade to airtable.js 0.8.1.

@namgk
Copy link

namgk commented Feb 22, 2021

any updates on this?

@Aias
Copy link

Aias commented Feb 28, 2021

I believe this is fixed as of ^0.10.1. I've been using the latest version without issue.

@jaequery
Copy link

jaequery commented Nov 2, 2021

Hi, I'm getting the same issue on 0.11.1.

I am trying to use Airtable on Nest.js btw.

$ yarn test:mocks
$ TZ=UTC jest --config ./test/mocks/jest-mocks.json
 FAIL  test/mocks/all.load.ts
  ● Test suite failed to run
    > 6 | import * as Airtable from 'airtable';
      at Object.<anonymous> (../../node_modules/airtable/src/fetch.ts:5:76)
      at Object.<anonymous> (../../node_modules/airtable/src/base.ts:4:1)
      at Object.<anonymous> (../../node_modules/airtable/src/airtable.ts:1:1)
      at Object.<anonymous> (../../src/user/user.service.ts:6:1)

any help would be appreciated, thanks!

@namgk
Copy link

namgk commented Nov 2, 2021

try the new node-fetch version 3, e.g. yarn add [email protected].

then remove the node_modules/airtable/node_modules

@TaylorFacen
Copy link

I'm running into this issue now with airtable v0.11.4

I have "node-fetch": "^3.0.0-beta.9", in my package.json. However, when I deploy my serverless function, airtable still uses v2.6.7 of node-fetch.

It would be great if the dependency was updated

@calvin-tung
Copy link

Any update on this issue? I have been working on this

@calvin-tung
Copy link

calvin-tung commented Jul 21, 2022

My Workaround:

Hosting my own node-fetch 2.6.7 and airtable, changing airtable's dependency to my own node-fetch and delete that few lines on node-fetch that caused error.

@someramsey
Copy link

I am able to get around the problem by using node compact mode on the wrangler.
The reason that airtable is not working is because that it uses the default ES6 modules but those modules are trimmed in wrangler so you need to import them separately or include them in the package.

importing doesn't work so you can only include them internally which has some severe tradeoffs

to include the modules you need to add this line to your wrangler.toml file
node_compat = true

but doing so has the following tradeoffs actual documentation
† the http and https modules are actually the same and don't differentiate based on protocol

‡ default export only, because it's console, seriously just use the global

§ vm does not have all corner cases and has less of them in a web worker

∆ not shimmed, just returns mock

@anridev24
Copy link

The issue has been open since Aug 6, 2020, please fix issue with that node-fetch package.

@bridger
Copy link

bridger commented Dec 16, 2022

This should be fixed by our latest release, v0.11.6

@maxcan
Copy link

maxcan commented Jan 27, 2023

This should be fixed by our latest release, v0.11.6

It is not.

@BridgerMaxwell-at
Copy link

Hey @maxcan, sorry this is still happening for you! Can you share more about the environment where this error is coming up? Is it in a browser, on a server, in a web worker, etc?

This error happens because we have a polyfill for environments that have fetch or not, or have a version of fetch which doesn't support AbortController. We need to know the exact environment to figure out when this polyfill should be activated or not.

@anridev24
Copy link

Hey @BridgerMaxwell-at, I'm getting error on AWS Lambda Functions, I'm using Serverless Framework 3.*, it worked fine locally with the "serverless-offline" plugin, right now I'm using "[email protected]" it has no error

@laurentperroteau
Copy link

It still not work in Node.js 18 in AWS Lambda with version 0.12.1 but I found a workaround overriding node-fetch dependencies to v3:

Available since npm cli v8.3.0

    "airtable": "0.12.1"
  },
  "overrides": {
    "airtable": {
      "node-fetch": "^3.3.1"
    }
  }

@manuarora700
Copy link

Worked for me - Next.js 14, Airtable 0.12.2

const nextConfig = {
  experimental: {
    esmExternals: "loose",
  },
};

@clawrence121
Copy link

Working for me in Next 14: #386 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests