17

I am trying this in the AWS lambda console. I have installed npm install @aws-sdk/client-kinesis on my terminal and used zipped the file and created a lambda layer which has client-kinesis.

If use the following it works!

 // ES5 example
const { KinesisClient, AddTagsToStreamCommand } = require("@aws-sdk/client-kinesis");
exports.handler = async (event) => {
    // TODO implement
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

If I am using the following it is giving me errors-

//ES6+ example 
import { KinesisClient, AddTagsToStreamCommand } from "@aws-sdk/client-kinesis";
     
exports.handler = async (event) => {
    // TODO implement
    const response = {
       statusCode: 200,
       body: JSON.stringify('Hello from Lambda!'),
    };
    return response; 
};
     
"Runtime.UserCodeSyntaxError: SyntaxError: Cannot use import statement outside a module",

Question -

  1. How to make this work from the AWS lambda console ?
  2. Is there any harm in using as the ES5 vs ES6 ? is it only syntax or there are performance issues also?

Thanks !

5 Answers 5

23

To upgrade to Node.js 18, you have to change the code to make the function work again. I don't use zip files, config files or layers.

Steps to upgrade your AWS Lambda function:

1. Change the filename of index.js to index.mjs

File names ending in .mjs are always treated as ES modules. File names ending in .js inherit their type from the package.

Reference: https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/

You should rename your index.js file to index.mjs to prevent one of the following errors:

  • SyntaxError: Cannot use import statement outside a module

  • SyntaxError: Unexpected token 'export'

2. Change your handler from CommonJS module handler to ES module handler

You must update the way you define your handler to prevent one of the following errors:

  • Runtime.HandlerNotFound: index.handler is undefined or not exported

  • ReferenceError: exports is not defined in ES module scope

Reference: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html

// Before NodeJS 18
exports.handler = async function (event, context) {
  console.log("EVENT: \n" + JSON.stringify(event, null, 2));
  return context.logStreamName;
}; 

// NodeJS 18 and above
export const handler = async (event, context) => {
  console.log("EVENT: \n" + JSON.stringify(event, null, 2));
  return context.logStreamName;
};

3. Change the way you import your modules

You cannot use the "require" keyword anymore. You will get the following error when you do:

  • ReferenceError: require is not defined in ES module scope, you can use import instead

It is important to know that since NodeJS 18 you cannot import the complete AWS SDK anymore, but you have to specify which modules you need, or you will get the following error:

  • Runtime.ImportModuleError: Error: Cannot find module 'aws-sdk'

Reference: https://aws.amazon.com/blogs/compute/node-js-18-x-runtime-now-available-in-aws-lambda/

// Before NodeJS 18
const AWS = require("aws-sdk");
const https = require("https");
const querystring = require("querystring"); 

// NodeJS 18 and above. In this example the module "client-sns" is imported from the AWS SDK.
import * as AWS from "@aws-sdk/client-sns";
import https from "https";
import querystring from "querystring";
Sign up to request clarification or add additional context in comments.

Comments

17

In order to fix the error, you could opt to use at least NodeJS version 18 or the latest runtime version (NodeJS 22)

  1. You can upgrade your old functions by adding "type": "module" to your package.json file. This will inform the Node runtime to use ES6 modules instead of the traditional ES5/CommonJS syntax.

  2. Change the extension of your files to .mjs to force the NodeJS runtime to treat your file as an ES6 module.

  3. If you are using esbuild, you will have to make sure that your build output is compatible with the ES6 modules (import/export).

  4. If you were using TypeScript, configure your transpiler to produce a build output that is compatible with ES modules.

  5. If there is a performance issue, it would be minimal that we don't have to worry about it! PS: I recommend to test the compatibility of the ES5/CJS/CommonJS modules that the lambda uses when you change the runtime.

3 Comments

Since package.json file is not in my AWS lambda console, so I will have to do a ZIP file deployment. Correct ?
@Judi, yes! You just need to add a package.json and configure the type property into module to fix this issue
I am a bit confused. Please help by putting the steps. Where do I start ? vscode or AWS Lambda console ? I tried adding a package.json file from the console and it did not work.
1

Tl;Dr: To deploy your second example, you will need to make some configuration changes that will require you to upload ZIP files for future deployments. Changes to the files between versions will be done within your IDE before being sent up to Lambda.

The long road (done via console and IDE): Your second example if failing because NodeJS defaults to ES5 require syntax while ES6 switched to import syntax; since you mentioned VSCode, here's something promising I saw. If that doesn't work then to use the import syntax seen in your second example, you will need to tell Node about this expectation. This is configured via a package.json file in the app's rootdir and enables various configuration options, as seen here. To do that, you can run npm init or yarn init depending on your package manager of choice. If you don't know much about either, I'd say yarn is more newb friendly but YMMV.

Now, the whole purpose of all the above YakShaving is solely to allow you a place (ie. inside package.json) to paste:

{ "type": "module" }

When adding 'type': 'module' in the package.json file, you're using import syntax by enabling ES 6 modules. When you do not have this, you should only use require syntax.

Comments

1

For me, just setting "type" to "module" on package.json on top directory does not work for ".js" files.

package.json

  "type": "module",

Somehow, when I make directory like "dist" and put javascript file like "dist/index.js", and set type to module on "dist/package.json" like below solve the issue. By that, ".js" files under "dist" directory are handled as ESM(modules). (I tested on Node20.)

dist/package.json

{
  "type": "module"
}

1 Comment

I had high hopes for this solution, but unfortunately it did not work for me. Despite using "ESM" syntax throughout and deploying successfully my lambda always throws the "cannot use Import outside of a module" error...
0

I had the same issue using Node18.

I fixed the issue without the need to define "type": "module" in package.json.

Changing tsconfig.json with the following fixed it :

{
  "extends": "@tsconfig/node18/tsconfig.json",
  "compilerOptions": {
    "experimentalDecorators": true,
    "outDir": "dist"
  },
  "inlineSource": true,
  "sourceMap": true,
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules"]
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.