aws-cdk
aws-cdk copied to clipboard
lambda: Cannot create ES Module function w/Code.fromInline
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
Yes this could be a solution. We'll look into this.
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
}
})
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.
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"
}
}
})
Hi @jolo-dev
Currently Code.fromInline only works to render code as a CloudFormation ZipFile prop:
https://github.com/aws/aws-cdk/blob/c66e197f6f8840da6475383dbf2421c3b06ea417/packages/aws-cdk-lib/aws-lambda/lib/function.ts#L902-L911
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
@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
Ran into this same limitation. CDK created index.js, not index.mjs when I used the fromInline function.
This is a consequence of the underlying CloudFormation support for inline Lambda functions. Please see https://github.com/aws-cloudformation/cloudformation-coverage-roadmap/issues/1420 for updates.