Search code examples
node.jsamazon-web-servicesaws-lambdaamazon-cloudfrontserverless

AWS Lambda Node ES6 common JS issue


Using nodejs18.x runtime in AWS Lambda using Serverless I have an issue with ES6 and Common JS.

When not setting type: module in package.json Lambda complains about using import statements outside of the module.export

SyntaxError: Cannot use import statement outside a module

But when I do set the type to module in package.json it complains that I am using required. Which I am not doing, but the s_tag_file.js file which is created by Serverless does contain required

{
    "errorType": "ReferenceError",
    "errorMessage": "require is not defined in ES module scope, you can use import instead\nThis file is being treated as an ES module because it has a '.js' file extension and '/var/task/package.json' contains \"type\": \"module\". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.",
    "stack": [
        "ReferenceError: require is not defined in ES module scope, you can use import instead",
        "This file is being treated as an ES module because it has a '.js' file extension and '/var/task/package.json' contains \"type\": \"module\". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.",
        "    at file:///var/task/s_tag_file.js:2:21",
        "    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)"
    ]
}

What am I doing wrong? Or what could I be doing differently?

Here is my full code:

package.json:

{
  "dependencies": {
    "@aws-sdk/client-s3": "^3.391.0",
    "aws-sdk": "^2.1437.0"
  },
  "type": "module"
}

serverless.yaml:

org: OUR_ORG
app: OUR_APP
service: OUR_SERVICE

frameworkVersion: '3'


provider:
  name: aws
  runtime: nodejs18.x
  lambdaHashingVersion: 20201221
  region: us-east-1 # Required for using CloudFront, region does not matter though (it runs at the CF edge location anyways)
  iamRoleStatements:
  cloudFront:
    cachePolicies:
      cdnCachePolicy:
        MinTTL: 0
        MaxTTL: 5184001
        DefaultTTL: 5184000
        ParametersInCacheKeyAndForwardedToOrigin:
          CookiesConfig:
            CookieBehavior:
              none
          EnableAcceptEncodingBrotli: true
          EnableAcceptEncodingGzip: true
          HeadersConfig:
            HeaderBehavior: none
          QueryStringsConfig:
            QueryStringBehavior: none

custom:
  origin: 'ORIGIN_URL'
  path: 'build'

functions:
  tag-file:
    handler: index.tagFile
    events:
      - cloudFront:
          eventType: viewer-response
          origin:
            DomainName: ${self:custom.origin}
            OriginPath: /${self:custom.path}
            CustomOriginConfig:
              OriginProtocolPolicy: match-viewer
          cachePolicy:
            name: cdnCachePolicy

index.js:

import { S3Client, PutObjectTaggingCommand } from "@aws-sdk/client-s3";

export async function tagFile(event, context, callback) {
  const response = event.Records[0].cf.response;

  const client = new S3Client({ region: "eu-west-1"});
  const input = {
    Bucket: "BUCKET_NAME",
    Key: event.Records[0].cf.request.uri,
    Tagging: {
      TagSet: [
        {
          Key: "requestTime",
          Value: Date.now().toString(),
        },
      ],
    },
  };
  const command = new PutObjectTaggingCommand(input);
  const S3response = await client.send(command);
  console.log(S3response);

  callback(null, response);
};

Solution

  • Since you are using org key (line 1) in your serverless.yml file, you are using Serverless Dashboard plugin.

    This plugin do not have ES6 support (which is required for having import statements). Refer to https://github.com/serverless/dashboard-plugin/issues/564 for more details.

    On a site note, Serverless Dashboard is being depreciated in favour of Serverless Console, which comes with ES6 support.

    So if you need to support ESM:

    1. Stop using Serverless Dashboard (since this plugin has been deprecated)
    2. Replace Serverless Dashboard with Serverless Console