TypeScript and Node AWS Lambda with Serverless

This guide assumes you have TypeScript and Node installed as well as an AWS account. We’ll be using serverless to set up our development environment and to package our app for deployment.

From mkdir to Deploy

We’ll install serverless and configure our AWS credentials. After that we’re ready for our first deploy.

First, let’s install the serverless CLI:

npm i serverless -g

We can now generate the minimum deployable boilerplate for our app:

mkdir node-typescript-lambda && cd node-typescript-lambda
serverless create --template aws-nodejs

Most importantly, we now have a simple Lambda function in handlers.js and a serverless.yml which will tell serverless how to deploy our function to AWS.

Now let’s configure our AWS credentials so we can deploy our function. If you already have an access key and secret access key in ~/.aws/credentials then you can skip this step. First, sign in to the AWS console. At the top right, click on your name to toggle the dropdown and navigate to “My Security Credentials. Then on the left navigation pane, click on “Users” and select a user with appropriate access. Click on the “Security credentials tab” to bring up the pane we’re interested in. Under the section “Access keys” click “Create Access key”

Take note of the two keys you’ve generated and then run the following command in your terminal, replacing YOUR_ACCESS_KEY and YOUR_SECRET_KEY with their actual values.

serverless config credentials --provider aws --key YOUR_ACCESS_KEY --secret YOUR_SECRET_KEY

You can now choose to deploy with serverless deploy, but be warned this will count as a request made on your AWS account, and may incur a charge.

Serverless plugins

We’re going to be using two serverless plugins. serverless-offline will allow us to test changes to our app locally. serverless-plugin-typescript will automatically compile our TypeScript files down to JavaScript both when we're developing locally and when we deploy our app.

To keep your deployment payload small, it’s important to declare these plugins as dev dependencies, otherwise serverless will install them before deploying them, increasing your lambda function’s size from <1kB to >30MB 👈

npm init
npm i serverless-offline serverless-plugin-typescript --save-dev

We’ll run our local environment with severless offline start but first we we need to edit serverless.yml to include the plugins we just installed.

Anywhere at the lowest indentation level in serverless.yml include these lines.

- serverless-plugin-typescript
- serverless-offline

If you were to remove serverless-plugin-typescript you would be able to start your dev server, but we'll first need a tsconfig.json to tell the TypeScript compiler what to do. While we're at it, let's install the type definitions for aws-lambda.

If you need something from node_modules, very useful plugin is serverless-plugin-optimize. It will pack your modules into one file and optimize for deployment.

tsc --init
npm i @types/aws-lambda --save-dev

You can configure your tsconfig.json to your personal preference, the only requirement is: "rootDir": "./" so that the compiler knows to look for .ts files in the current directory. If you choose to move all your .ts files to a new folder called ./src, for example, make sure to change the setting to "rootDir": "./src". You can also set "es6" as your compilation target because Lambda uses Node 6, which supports it. I've included a sample tsconfig.json here:

"compilerOptions": {
"module": "commonjs",
"target": "es6",
"lib": [ "es6", "dom" ],
"moduleResolution": "node",
"rootDir": "./",
"sourceMap": true,
"allowJs": true,
"noImplicitAny": true,
"noUnusedLocals": true,
"noImplicitThis": true,
"strictNullChecks": true,
"noImplicitReturns": true,
"preserveConstEnums": true,
"suppressImplicitAnyIndexErrors": true,
"forceConsistentCasingInFileNames": true
"exclude": [ "node_modules", "build", "webpack" ],
"types": [ "typePatches" ]

Adding Types

Our handler.js could use a makeover. Let's rename it to handler.ts and run our development server with serverless offline start.

We’ll first import the relevant types from 'aws-lambda' and then add types to each of the function's parameters as well as the response object.

import { Handler, Context, Callback } from 'aws-lambda';
interface HelloResponse {
statusCode: number;
body: string;
const hello: Handler = (event: any, context: Context, callback: Callback) => {
const response: HelloResponse = {
statusCode: 200,
body: JSON.stringify({
message: Math.floor(Math.random() * 10)
   callback(undefined, response);
export { hello }

With these type definitions, our editor will loudly warn us when we try to add a key that shouldn’t exist and provide suggestions based off the type definitions.

We’re almost ready to deploy this test function, but first let’s exclude node_modules from our serverless.yml so we don't deploy aws-lambda and all of its dependencies.

Anywhere at the lowest indentation level of serverless.yml add the following lines:

- node_modules/**/*

Making Our Lambda Function an Endpoint

By editing our serverless.yml once more, we can configure our Lambda function to be a visitable URL with AWS APIGateway.

handler: handler.hello

- http:
path: hello
method: get

By adding the last four lines, we configure our hello Lambda function to expose the path /hello to an HTTP GET request. If we run serverless deploy now it will generate a URL that you can visit in your browser.

The output of the serverless CLI showing us the URL endpoint for our lambda function.

🗣 Credits: Shovon Hasan (https://blog.shovonhasan.com)
👁 Reviewed and fixed by: Satoshi+
Like what you read? Give Satoshi+ a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.