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.