When we are using NodeJS as the runtime for AWS Lambdas, JavaScript is the default language. However, due to the lack of typing check in JavaScript, from time to time, buggy codes are deployed to Lambda inadvertently. Such as a small typo like this:
exports.lambdaHandler = async (event, context) => {
const queries = event.queytStringParameters;
// ...
}
We mean to get queryStringParameters, but the queries ends up undefined as a result of carelessness.
We want to utilize TypeScript to write lambda handlers. With TypeScript, we will have the following benefits:
Vanilla code completion hints while programmingCompilation time error checks to avoid redundant deployments
It is not hard to do so, this article will introduce 3 steps to complete that job.
Before going through the 3 steps, let’s create a classic lambda project with SAM CLI:
sam init
After the above command, we will get a folder with these files:
├── README.md
├── events
│ └── event.json
├── hello-world
│ ├── app.js
│ ├── package.json
│ └── tests
│ └── unit
│ └── test-handler.js
└── template.yaml
Then we will start shift this JS package into a TS package.
Step 1: Add TypeScript Dependency
In the package.json, add the following codes:
"scripts": {
"compile": "tsc"
},
"devDependencies": {
"aws-sdk": "^2.655.0",
"@types/aws-lambda": "^8.10.51",
"@types/node": "^13.13.5",
"typescript": "^3.8.3"
}
Step 2: Add tsconfig.json
{
"compilerOptions": {
"module": "CommonJS",
"target": "ES2017",
"noImplicitAny": true,
"preserveConstEnums": true,
"outDir": "./built",
"sourceMap": true
},
"include": ["src-ts/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
TypeScript compiler needs tsconfig.json to figure out how to transform TypeScript to JavaScript.
Step 3: Change the code
First, create the folder src-ts, and move app.js to that folder.
The app.js looks like this now:
exports.lambdaHandler = async (event, context) => {
const queries = JSON.stringify(event.queytStringParameters);
return {
statusCode: 200,
body: `Queries: ${queries}`
}
};
Let’s create app.ts and replace it:
import {
APIGatewayProxyEvent,
APIGatewayProxyResult
} from "aws-lambda";
export const lambdaHandler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
const queries = JSON.stringify(event.queryStringParameters);
return {
statusCode: 200,
body: `Queries: ${queries}`
}
}
Since this TypeScript file will be compiled to built folder, we also need to modify the Handler field in template.yaml, to ensure the lambda resource could locate the code in the right path:
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/built
Handler: app.lambdaHandler
We append the path built by the folder hello-world, so that AWS Lambda could find the handler correctly.
Now the directory looks like below:
├── README.md
├── hello-world
│ ├── built
│ │ ├── app.js
│ │ └── app.js.map
│ ├── package-lock.json
│ ├── package.json
│ ├── src-ts
│ │ ├── app.ts
│ │ └── tests
│ └── tsconfig.json
├── samconfig.toml
└── template.yaml
Deploy and Test
cd hello-world
npm install
npm run compile
cd ..
sam deploy --guided
After deployed successfully, we will see the Lambda function in AWS Console as follows:
And we should be able to test the Lambda function using the following command:
▶ curl https://[API_ID].amazonaws.com/Prod/hello\?weather\=sunny
Queries: {"weather":"sunny"}
Conclusion and Next Step
I find it really refreshing after utilizing TypeScript support for my Lambda functions. On one hand, it could save me tons of time looking up the API Doc for a method name or the parameter list. On the other hand, tsc also helps me to detect any potential problem before deploying.
In the next article, I will talk about how to do local integration tests on Lambda functions.