Search code examples
node.jsamazon-web-servicesaws-lambdaairtableaws-lambda-layers

Problems using airtable.js module in AWS lambda


I'm trying to access Airtable from an AWS lambda function.

First off, to test, I installed airtable.js (npm install -s airtable) in a local project, wrote a short test script, and executed with node test.js. All works fine.

Using exactly the same core test code, inside an appropriate node.js function wrapper, I've tried running the same test in an AWS lambda function, and I get an error in my CloudWatch logs:

Error: Cannot find module '/var/task/node_modules/abort-controller/dist/abort-controller'. Please verify that the package.json has a valid \"main\" entry

I've tried both zipping the npm packages up with the function code in a deployment package, and also creating a lambda layer from the airtable package. Both produce the same error. Note that the package is picked up - if I try the layer approach, but removing the layer itself then it can't find airtable. So this seems to be something specific with how the airtable package is trying to access abort-controller.

For what it's worth, here's the [redacted] test code that I'm using in my lambda function: (the returns etc are because it's operating behind an API gateway call - but that's not part of the issue because the same error occurs regardless of whether testing inside the lambda console or calling through the API)

var AWS = require("aws-sdk");
AWS.config.update({ region: "eu-west-1", });

const Airtable = require('airtable');
const base = new Airtable({ apiKey: "xxxxx" }).base('yyyyy');

exports.handler = async(event) => {
    try {
        console.info('trying create');
        let records = await base('Base1').create([{
            "fields": {
                "Status": "Scored",
                "C1": "testing",
                "C2": "airtable",
                "C3": "api",
                "C4": "from",
                "C5": "node",
            }
        }, ]);
        console.info('completed create');
        let records_list = records.map(r => r.getId()).join(',');
        console.info(records_list);
        return ({statusCode: 200, body: JSON.stringify(records_list)});
    } catch (e) {
        console.error(e);
        return ({statusCode: 401, body: JSON.stringify(e)});
    }

}

I've built many lambdas previously, both with layers and with embedded packages, and so I've made most of the common mistakes - and I don't think I'm repeating them here. Is there anything special about airtable.js which means there's a different approach needed here?


Solution

  • Turns out the problem was in the zip of the deployment package - whether in a layer or baked into the lambda, the zip file seems to have been missing something. I was doing that as part of my terraform configuration / deployment, and what's perplexing is that it seems to be exactly the same structure and setup as I've used successfully for over 20 functions and 5 layers in a different project, but here it's failing.

    So - solution seems to be, for the moment at least, to manually zip the layer package, upload to s3, and then get terraform to pick it up from there.