The AWS Cloud Development Kit (CDK) lets you build serverless applications with the expressive power of a programming language like TypeScript. The CDK defines cloud infrastructure in code and deploys via AWS CloudFormation.
In this post, we will build a Lambda function, an AWS Gateway API, and an S3 bucket to upload CSV files. The API will take requests in JSON and seamlessly convert them to CSV format. We will use the AWS CDK in TypeScript to reliably deploy our app through AWS CloudFormation.
Ready? Let’s go!
Quick Start
Some familiarity with Node and TypeScript is expected. You can follow along by copy-pasting the code or by cloning this GitHub repo.
The first thing you will need is the CDK in Node.
This global tool lets you invoke the AWS CDK via the cdk
command.
Next, initialize the project. You can name it aws-typescript-cdk
.
This single cdk
command should spin up an entire TypeScript project for you, including the tsconfig.json
file, package.json
, CDK dependencies, and unit tests in Jest.
The files of interest are:
bin/aws-typescript-cdk.ts
: Main entry point of the app to run the CDK.lib/aws-typescript-cdk-stack.ts
: Defines the CDK stack that provisions in CloudFormation.test/aws-typescript-cdk-test.ts
: Unit test file to validate the stack and shorten the feedback loop.
To spin up the app that will be provisioned with this CDK, create a resources
folder and simply create a lambda.ts
file. You should end up with a resources/lambda.ts
folder structure from the root folder. In the test
folder, create a matching test/lambda.test.ts
file for the unit tests.
To validate what we have so far, use the synth
command.
This outputs the resources in the CDK. Pay close attention to the format:
This declares which resources will deploy via CloudFormation, and you can use it as a good reference for what gets included in the CDK. We mostly only care about the Type
and Properties
.
Lambda Function
Open the lambda.ts
file and add the code below. Note the REGION
: be sure to specify a correct region, like us-east-2
, that is closest to your geographical location.
The S3 bucket comes from an environment variable that we can set in the CDK. The actual conversion into CSV takes the request as a JSON array in the body.
If you get a compiler error because it can’t find the APIGatewayProxyEventV2
type, simply add the @types/aws-lambda
dependency via NPM. Also, be sure to add @aws-sdk/client-s3
to get the S3 client.
For example:
Now, to validate these changes, simply write the unit test. Open the lambda.test.ts
file and put this in:
What we have so far is a basic Lambda function that makes assumptions about the underlying infrastructure. It assumes there is an S3 bucket and takes in an AWS Gateway event parameter.
CDK Service in AWS
To declare what resources get deployed via CloudFormation, create a lib/aws-cdk-service.ts
file right in the same folder as the CDK stack file.
We will now create a service that extends from a Construct
, then a new Bucket
, Function
, and RestApi
. These are the resources in AWS that are necessary for our Lambda function.
The S3 bucket name is automatically generated and put in the Lambda function environment variables. This gets us over the limitation of having to create a unique bucket name.
Luckily, the AWS CDK comes with a very nice suite of tools, so we can write unit tests in TypeScript. Create a matching test/aws-typescript-cdk.tests.ts
file and validate what must be in the CDK.
Now, run npm t
and then cdk synth
. Go and inspect the output from the synthesizer. You should be able to find resources like AWS::S3::Bucket
and AWS::Lambda::Function
. Some of these resources also have properties we can validate in the unit test via template.hasResourceProperties
. This makes it seamless for you to write your CDKs and validate what resources must be available.
The Template
assertion library also catches trivial, hard-to-spot mistakes in the CDK.
When the test fails because it can’t find a match, the tooling shows you where it finds the closest match so you can fix the test.
For example:
Note that the Function
construct grabs everything in the resources
folder and puts it in the Lambda function. The bundle size remains small because Node Lambdas already include the AWS SDK. Alternatively, you can use NodejsFunction
to provision the Lambda function: this uses esbuild to bundle up the script and optimize cold starts.
Deploy and Test Your TypeScript App
Open the aws-typescript-cdk-stack.ts
file, then include the service in the Stack
class constructor. Be sure to import
the service class in TypeScript.
Also, we recommend setting the stack id to something memorable and unique like aws-typescript-cdk-stack
. This is because changing the stack id will force you to delete the entire stack with all its resources.
You can find this stack id in the main entry point in the bin
folder.
For example:
Then, flesh out the main entry point in aws-typescript-cdk.ts
. Simply uncomment the env
property and put in your account number and region.
Now, bootstrap the CDK using the following command:
We are now ready to deploy the app:
You can track progress in the AWS console by navigating to CloudFormation (which should have a stack name like aws-typescript-cdk-stack
with status information).
To test the app, spin up CURL:
Look for a 202 HTTP status code, then validate that there is an S3 bucket with a car-models.csv
file in it. Keep in mind the bucket name is randomly generated.
And that's it!
Wrapping Up
As we've seen in this post, the TypeScript AWS CDK is a nice way to write infrastructure code that gets deployed in AWS. The tooling makes it seamless to create, unit test, and deploy resources.
Happy coding!
P.S. If you liked this post, subscribe to our JavaScript Sorcery list for a monthly deep dive into more magical JavaScript tips and tricks.
P.P.S. If you need an APM for your Node.js app, go and check out the AppSignal APM for Node.js.