Geek Culture
Published in

Geek Culture

Complete End-to-end Guide for Developing Dockerized Lambda in Typescript, Terraform and SAM CLI

Docker + TS + Terraform + Lambda + ECR + SAM CLI = 💚

Reason to use Terraform and SAM CLI together

Setup AWS for Terraform

Add user
Create policy

Setup Terraform

Setting up lambda

  • @types/node: we will need this to give proper type definitions for 'built-in node' modules like fs or path.
  • concurrently: just a script runner.
  • lerna: you know it.
  • nodemon: this will help us watch a directory and build Docker image again.
  • amazon/aws-lambda-nodejs:14 is the official amazon image for lambda. Current LTS of nodejs is 14, so we are using this. AS builder is related to multi-stage builds in Docker; it helps reduce the final Docker image size. Basically in this builder stage, we will only build the output to be included in the final image, and any dependencies installed in this step won't be included in the final output image.
  • WORKDIR /usr/app: inside the docker image, set the working directory as /usr/app. There isn't any app folder in a normal docker image, so it will make app directory. We will put the compiled js code there.
  • COPY package*.json tsconfig.json ./: we need these files for compiling typescript into javascriptt files.
  • npm install: it will install dependencies.
  • npm run build: it will compile typescript code into js.
  • RUN ls -la # for debugging: it is merely for debugging. While building, docker will output what's inside there at that time, for you to verify if you are doing what you intended to do.
  • FROM amazon/aws-lambda-nodejs:14: this is the second build stage in Docker. All outputs from the previous stage will be discarded in this stage unless explicitly specified to be included.
  • RUN npm install --only=prod: it will only install dependencies but devDepdencies.
  • COPY --from=builder /usr/app/lib /usr/app/lib: it explicitly refers to the previous builder stage to copy whatever that was inside /usr/app/lib to the current /usr/app/lib. In this case, it will copy all compiled javascript code.
  • CMD [ "/usr/app/lib/index.handler" ]: the command should be path-to-lambda-handler-without-extension.handler. That's just how it works.
Invoking lambda locally

Back to Terraform: assuming role and ECR

Building and pushing the image to ECR

Images on ECR
Check lambda created on AWS console #0
Check lambda created on AWS console #1
APIs tab
REST resource
  1. Use timestamp() to trigger redeployment for every single apply. Using this approach, lambda may be down for few seconds while redeployment. But it is for sure that it always deploys, so I would just go with this one if my service does not handle many users.
  2. Use md5(file("")) to trigger redeployment whenever this file changes. But you need to always make sure that EVERYTHING related to API Gateway deployment only stays inside this file.
Invoke URL
  1. Insert your invoke URL
  2. Click ‘Authorization’, and choose the type ‘AWS Signature’, and enter AccessKey and SecretKey. These keys should be coming from one of user's credentials from AWS IAM Console. If you do not have one specialized for calling an API set up with lambda and API gateway from local environment, make one user for that and get the keys.
  3. Insert your AWS Region.
  4. If your API requests any more query parameters or body, insert them.
  5. Click send, then it should work.
Testing ‘Invoke URL’ on Postman

Creating Terraform resources for custom domain
AWS Certificate Manager
API Gateway domain name
Route53 record for to
API mapping on API Gateway

Enabling OPTIONS (preflight request)

Image from

Summing up



A new tech publication by Start it up (

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Joel Mun

High quality article or death. Code/Security stuffs, mostly related to React, Typescript, and Node, GoLang, Wasm.