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

lambda: Cannot create ES Module function w/Code.fromInline #27980

Open
krk opened this issue Nov 14, 2023 · 8 comments
Open

lambda: Cannot create ES Module function w/Code.fromInline #27980

krk opened this issue Nov 14, 2023 · 8 comments
Labels
@aws-cdk/aws-lambda Related to AWS Lambda feature-request A feature should be added or improved. p2

Comments

@krk
Copy link

krk commented Nov 14, 2023

Describe the bug

Lambda Node Runtimes support loading ESM code since the nodejs14.x runtime.

If there is a single file in the zip, it needs to have the .mjs extension to be loaded as an ES module. Other option is providing a package.json file with "type": "module" in it.

Currently, there is no way to tell CDK to set the file extension when lambda.Code.fromInline is used:

Expected Behavior

Not getting Runtime.UserCodeSyntaxError from the lambda invocation.

Current Behavior

Error on lambda invoke:

{
  "errorType": "Runtime.UserCodeSyntaxError",
  "errorMessage": "SyntaxError: Cannot use import statement outside a module",
  "trace": [
    "Runtime.UserCodeSyntaxError: SyntaxError: Cannot use import statement outside a module",
    "    at _loadUserApp (file:///var/runtime/index.mjs:1084:17)",
    "    at async UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1119:21)",
    "    at async start (file:///var/runtime/index.mjs:1282:23)",
    "    at async file:///var/runtime/index.mjs:1288:1"
  ]
}

only because the file name in the deployment package is index.js.

Reproduction Steps

new lambda.Function(this, "Verso-Cacher", {
      functionName: "esm",
      code: lambda.Code.fromInline("import { DynamoDBClient } from '@aws-sdk/client-dynamodb'",
      handler: "index.handler",
      memorySize: 2048,
      timeout: Duration.seconds(60),
      runtime: new lambda.Runtime("nodejs18.x", lambda.RuntimeFamily.NODEJS, {
        supportsInlineCode: true,
      }),
    });

Deploy and invoke the function.

This function handler doesn't do anything useful, other than importing from the SDK as an ES Module. It will deploy successfully and invocations will fail.

Possible Solution

Add a new argument in lambda.Code.fromInline that specifies the filename, index.mjs would fix it for the example.

Additional Information/Context

No response

CDK CLI Version

any

Framework Version

No response

Node.js Version

14,16,18

OS

any

Language

TypeScript

Language Version

No response

Other information

No response

@krk krk added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Nov 14, 2023
@github-actions github-actions bot added the @aws-cdk/aws-lambda Related to AWS Lambda label Nov 14, 2023
@pahud
Copy link
Contributor

pahud commented Nov 14, 2023

Yes this could be a solution. We'll look into this.

@pahud pahud added feature-request A feature should be added or improved. p2 needs-review and removed bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Nov 14, 2023
@jolo-dev
Copy link
Contributor

Another, IMO easier solution would be, to add Code to NodeJsFunction. Because then you could make use of BundlingOptions. But if code is set the entry should not be allowed.

new NodejsFunction(stack, 'MyFunc', {
  runtime: Runtime.NODEJS_LATEST,
  code: Code.fromInline("import { DynamoDBClient } from '@aws-sdk/client-dynamodb'"),
  handler: 'index.handler',
  bundling: {
    format: OutputFormat.ESM
  }
})

@tmokmss
Copy link
Contributor

tmokmss commented Nov 18, 2023

Is it even possible to set filename or extension when using ZipFile? I'm afraid not, since there is no such property on the CFn definition. Maybe needs-cfn.

@jolo-dev
Copy link
Contributor

Is it even possible to set a filename or extension when using ZipFile? I'm afraid not, since there is no such property on the CFn definition. Maybe needs-cfn.

Hmm... I may not follow. Could you elaborate? When we zip, we zip the whole folder. There, we don't care about the extension.
But here, in this case, it's about Code.fromInline where we don't need to zip, right?
Theoretically, we could use the esbuildArgs to add the file extension.

new NodejsFunction(stack, 'MyFunc', {
  runtime: Runtime.NODEJS_LATEST,
  code: Code.fromInline("import { DynamoDBClient } from '@aws-sdk/client-dynamodb'"),
  handler: 'index.handler',
  bundling: {
    format: OutputFormat.ESM,
    esbuildArgs: {
      "--outfile": "index.mjs"
    }
  }
})

@tmokmss
Copy link
Contributor

tmokmss commented Nov 19, 2023

Hi @jolo-dev
Currently Code.fromInline only works to render code as a CloudFormation ZipFile prop:

const resource: CfnFunction = new CfnFunction(this, 'Resource', {
functionName: this.physicalName,
description: props.description,
code: {
s3Bucket: code.s3Location && code.s3Location.bucketName,
s3Key: code.s3Location && code.s3Location.objectKey,
s3ObjectVersion: code.s3Location && code.s3Location.objectVersion,
zipFile: code.inlineCode,
imageUri: code.image?.imageUri,
},

so the constraints of CloudFormation ZipFile are directly imposed on Code.fromInline. The actual zip process (e.g. creating a file from the code string and zipping it) is done on CloudFormation side. Afaik currently you cannot set its filename but CFn decides it.

Is there any reason not to use code from a file? With code from a file, you can use NodejsFunction to get ESM code.
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs.BundlingOptions.html#format

@jolo-dev
Copy link
Contributor

@tmokmss You're right.
But since we're using Typescript, we could come up with something ;)

I once had a situation where I needed a simple Lambda to pass the event to trigger something.
However, it is not Software Engineering's best practice. Because you cannot really test it :D

@sigpop
Copy link

sigpop commented Feb 21, 2024

Ran into this same limitation. CDK created index.js, not index.mjs when I used the fromInline function.

@jtuliani
Copy link

This is a consequence of the underlying CloudFormation support for inline Lambda functions. Please see aws-cloudformation/cloudformation-coverage-roadmap#1420 for updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-lambda Related to AWS Lambda feature-request A feature should be added or improved. p2
Projects
None yet
Development

No branches or pull requests

7 participants