This is a "walking skeleton" AWS Lambda app, using TypeScript, CDK, Vitest, and GitHub Actions. It is fully deployable, includes tests, and has a Github Actions workflow that will perform remote tests on an ephemeral deployment in AWS.
In other words you can use this repo as a basis for making your own TypeScript Lambda-based applications.
This example is part of a collection of CDK examples I have created - others are as follows:
- CDK bare-bones app for TypeScript - Base project for any TypeScript app using CDK for deployment to AWS. Try this first if you are getting started with CDK.
- Coffee Store Web Basic - Website hosting on AWS with CloudFront and S3
- Coffee Store Web Full - A further extension of Coffee Store Web Basic that is a real working demo of a production-ready website project, including TLS certificates, DNS hosting, Github Actions Workflows, multiple CDK environments (prod vs test vs dev)
This example deploys a CDK App that deploys a Lambda Function, together with a Lambda Function URL to make it accessible over HTTP.
To build the Lambda function, this example uses the NodejsFunction
CDK Construct which performs build actions as part of the deploy process. In this configuration the Construct:
- Runs TSC in "noEmit" mode to perform type-checking
- Runs Esbuild to create an optimized artifact for the Lambda function
Using NodejsFunction
makes CDK a build tool and not just a deployment tool.
Fellow old-timey developers like me might be concerned by this, however I feel like NodejsFunction
is "good enough" for many small to medium size TS Lambda projects.
If you'd like more control over your build process then swap NodejsFunction
for the standard CDK Function
construct,
and add a build phase to your project. To see an example of this, including a wrapper script for ESBuild, see the earlier version of this project.
Please see the prerequisites of the cdk-bare-bones project - all of the prerequisites in that project also apply to this one.
After cloning this project to your local machine, run the following to perform local checks on the code:
$ npm install && npm run local-checks
If successful, the end result will look something like this:
✓ test/local/api.test.ts (2 tests) 2ms
✓ lambda function should return expected message when no name passed
✓ lambda function should return expected message when name on query string
Test Files 1 passed (1)
Tests 2 passed (2)
Start at 18:55:58
Duration 245ms (transform 30ms, setup 0ms, collect 28ms, tests 2ms, environment 0ms, prepare 47ms)
The unit tests run entirely locally, and execute the code directly, i.e. they do not use local runtime simulators. For the reasoning behind this choice see https://blog.symphonia.io/posts/2020-08-19_serverless_testing .
Once you've run npm install once in the directory you won't need to again unless you need to update versions of dependencies.
To deploy the application to AWS run the following:
$ npm run deploy
If successful, the end result will look something like this:
✅ CoffeeStore (coffee-store-cdk)
✨ Deployment time: 63.31s
Outputs:
CoffeeStore.ApiUrl = https://2mnu7mnuphryvz5io3dlbprsam0wmxqv.lambda-url.us-east-1.on.aws/
Stack ARN:
arn:aws:cloudformation:us-east-1:397589511426:stack/coffee-store-cdk/a8c8bb20-d852-11ef-afa7-0e0775607411
✨ Total time: 67.13s
That CoffeeStore.ApiUrl
value is a public URL - access it with your browser and you should see a "Hello World" message.
For other commands, including how to teardown, see the Usage section of the bare-bones project README.
- Install dependencies with
npm install
, or your IDE's Node support. - Run unit tests by running all tests under /test/local.
This project includes a remote test which calls the deployed app in AWS via https and validates the response.
If you want to run the remote tests against a stack that has already been deployed you should specify the stack's name with
the STACK_NAME
environment variable, e.g.:
$ STACK_NAME=coffee-store-cdk npm run remote-tests
If you want to run remote tests via the IDE:
- Use the vitest configuration at /test/remote/vitest.config.ts
- make sure to specify the
STACK_NAME
environment variable as part of your test runner configuration if you want to use a pre-deployed stack.
Alternatively the remote test can run against an ephemeral stack - i.e. a new stack will be deployed as part of
test setup, and then torn down as part of test cleanup. Not surprisingly this method takes a lot longer to run! To use this method don't
specify a STACK_NAME
value in the environment.
E.g. if you run npm run remote-tests
with no STACK_NAME
you will see something like the following in the console output
while the remote test is being run:
> [email protected] remote-tests
> npx vitest run --dir test/remote --config test/remote/vitest.config.mts
RUN v3.0.3 [PATH_HERE]/coffee-store-cdk
stdout | test/remote/api-remote.test.ts
Starting cloudformation deployment of stack coffee-store-it-20250121-191154
and a little later you will see:
stdout | test/remote/api-remote.test.ts
Calling cloudformation to delete stack coffee-store-it-20250121-191154
✓ test/remote/api-remote.test.ts (1 test) 70204ms
✓ API should return 200 exit code and expected content 355ms
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 19:11:54
Duration 70.52s (transform 29ms, setup 0ms, collect 81ms, tests 70.20s, environment 0ms, prepare 56ms)
The example above shows the test creating a stack named stack coffee-store-it-20250121-191154
. If you want an
alternative to the start of the name being coffee-store-it
you can specify
the STACK_NAME_PREFIX
environment variable when running the remote test.
The included Github Actions workflow at .github/workflows/buildAndTest.yml will run all tests. Since these tests include the remote tests, the workflow will (indirectly) deploy an ephemeral version of the application to AWS, and therefore the Github Actions workflow needs permission to access your AWS account.
For instructions on how to setup these permissions, see github-actions-prereqs/README.md.
Alternatively if you don't want to setup AWS integration for GitHub Actions change the workflow definition to just run local-checks
.
The included workflow will either run automatically whenever you push a change to the main
branch, or when you run
the workflow manually through the web console.
You might choose to update the workflow to also deploy a non-ephemeral version of the app. To do this add running
npm run deploy
to your workflow - in which case you may also want to specify the STACK_NAME
env var.
If you have questions related to this example please add a Github issue, or drop me a line at [email protected] . I'm also on Mastodon at http://hachyderm.io/@mikebroberts and BlueSky at https://bsky.app/profile/mikebroberts.com .
This is an updated version of a demo app I originally put together in 2020 for Cloud Coffee Break, a YouTube series I recorded.
This version contains the following changes since the 2020 version, plus several others:
- Uses TypeScript instead of Javascript
- Uses CDK instead of SAM
- Uses Github Actions instead of AWS CodeBuild for automation. Github Actions-to-AWS security uses OIDC, not long-lived access tokens.
- Uses a Lambda Function URL instead of API Gateway
- Renamed to "coffee-store-cdk" from "coffee-store-v2"
- Switch to Node 22 from 16
- Update package-lock to use latest versions of specified dependencies
- Use TypeScript 5
- Add ESLint and Prettier
- Switch to vitest from jest
- Separate "domain" code from Lambda code (mostly for demonstrating future ES module alternative)
- Introduce a little behavior that uses input event
- Extended tests to test extra behavior
- Moved and renamed some files / directories to fit how I've been working recently
- Add some Lambda configuration:
- ARM Architecture
- Specify memory size as 512 MB
- Specify Timeout as 5 seconds
- Use CDK Log Retention
- Switched to tsx from ts-node in CDK configuration (mostly for future ES module alternative)
- Removed no-longer used "import 'source-map-support/register'" from CDK
- Cleaned up unused libraries
- Update project dependency versions
- Move cdk.json to src/cdk directory. This is for a couple of reasons:
- One fewer file in project root, which I think is A Good Thing
- Makes it easier to have repos with multiple, separate, CDK apps
- Update lambdaFunction definition for new relative location of app source code
- This is necessary because of the previous change - CDK is going to be running in a different directory now
- Move "output" and "requireApproval" CDK settings from package.json to cdk.json
- I hadn't read the docs enough to know they could be in cdk.json. Oops. This way is cleaner
- Use CDK
build
step to run TSC instead ofpreCompilation
in NodejsFunction- Getting NodejsFunction to run tsc just for type-checking is problematic, but I had it working in previous version. I hadn't realized before that the "build" CDK property applied to all synth activities, not just "cdk watch", and so putting project-wide type checking in the "build" property, combined with still using NodejsFunction for running esbuild, is a cleaner solution. This allows cleaning up the previous workaround in tsconfig.json
- Update Node to Node 16 (tooling + runtime)
- Switch to CDK for building, by using NodejsFunction
- Standardize project setup vs https://github.com/symphoniacloud/cdk-bare-bones
- Update this README, referring to bare bones project to avoid duplication
- Remove username based stack naming to simplify things a little
- Uses TypeScript instead of Javascript, plus some opinionated build + package scripting
- Uses CDK instead of SAM
- Uses Github Actions instead of AWS CodeBuild for automation. Github Actions-to-AWS security uses OIDC, not long-lived access tokens.
- Uses a Lambda Function URL instead of API Gateway