AWS Lambda for years now supports NodeJS runtime and I was writing its code in JavaScript even if my language of choice in test automation would be TypeScript.
It was safer to follow tutorials and use index.js as an entry point to my Lambdas I just had to make sure to have linter configured to catch my mistakes as early as possible and that I followed documentation (and its correct version).
Now I figured out how to write (and deploy) functions written in TypeScript and wanted to share my learnings.
I will be using Terraform - the module terraform-aws-modules/lambda/aws - makes tasks of managing and deploying Lambda much easier. The module itself is written in Python (in case you see any Python-related errors) and handles installing dependencies and zipping your fuction for you.
Have you ever use pipline/workflow to zip your project? Or used provisioner "local-exec"
to execute npm i
and zip
before deploying Lambda? If yes, you will appriciate this Terraform module.
I will go through the steps of setting up the Lambda project as part of the Terraform IaC project (jump to Terraform if you want to skip this)
create src directory
1mkdir src && cd src/
intitatie new project (package manager of your choice)
1npm init -y
and update package.json of project related information - description, version, author etc
1touch index.ts
We will install two modules:
@types/aws-lambda This module will give us access to Lambda types in index.ts
and TypeScript both as dev dependencies
1npm install -D typescript @types/aws-lambda
Installing npm modules - Lambda is a lightweight solution, its small size is emphasised also during Lambda upload and has a maximum size set to 50MB (can be overridden with layers if needed). That's why we install additional modules as devDependencies to avoid unnecessary size. You can always check the size of your project by running (Linux/MacOS) in the root of your project
1du -sh
setup TS config:
1npx tsc --init
this will create tsconfig.json file. AWS recommended config
1{2 "compilerOptions": {3 "target": "ES2016",4 "strict": true,5 "preserveConstEnums": true,6 "noEmit": false,7 "module":"commonjs",8 "esModuleInterop": true, 9 "skipLibCheck": true,10 "forceConsistentCasingInFileNames": true, 11 "isolatedModules": true, 12 },13 "exclude": ["node_modules", "**/*.test.ts"]14}
In the package.json add script:
1...2"scripts": {3 "build": "npx tsc",4 },
And of course we need example code:
1import { Context, APIGatewayProxyCallback, APIGatewayEvent } from 'aws-lambda';23export const handler = (event: APIGatewayEvent, context: Context, callback: APIGatewayProxyCallback): void => {4 console.log(`Event: ${JSON.stringify(event, null, 2)}`);5 console.log(`Context: ${JSON.stringify(context, null, 2)}`);6 callback(null, {7 statusCode: 200,8 body: JSON.stringify({9 message: 'hello world',10 }),11 });12};
In this section, I assume you already using Terraform and have the project configured with the AWS provider and your pipeline plans and apply Terraform to your AWS account.
Create TF file that will contain lambda definition
1touch lambda.tf
The basic configuartion of AWS Lambda module may look like this
1module "lambda_function" {2 source = "terraform-aws-modules/lambda/aws"34 function_name = "my-lambda"5 description = "My awesome lambda function"6 handler = "index.lambda_handler"7 runtime = "python3.8"89 source_path = "../src/lambda-function"1011 tags = {12 Name = "my-lambda"13 }14}
Based on provided runtime and path to the function source module will install dependencies and zip project before uploading to AWS.
If we deploy it like this we could see an error in CloudWatch logs when try to execute this function: index.mjs has not been found.
This is because we need to transpile TypeScript code to a JavaScript module
and we can do it by running the previously set command npm run build
The Terraform Lambda module allows to override default runtime installing and zipping commands and we can provide them in a format like this:
1module "ts_lambda_function" {2 source = "terraform-aws-modules/lambda/aws"34 function_name = "ts-my-lambda"5 description = "My lambda function written in TS"6 handler = "index.handler"7 runtime = "nodejs18.x"8 source_path = [{9 path = "../src"10 commands = [11 "npm ci", # install dependencies12 "npm run build", # npx tsc to transpile13 "npm prune --production"14 ":zip" # zip all15 ]16 }]17 publish = true1819 environment_variables = {20 ENV = "dev"21 }22 attach_policy_statements = true23 policy_statements = {24 cloud_watch = {25 effect = "Allow",26 actions = ["cloudwatch:PutMetricData"],27 resources = ["*"]28 }29 }30 tags = {31 Name = "ts-lambda"32 }33}
From here you can attach the desired trigger for the lambda, role etc.
Hope those few code snippets help with your project!