Deploying Typescript Lambdas to AWS with the CDK

Phil Blenkinsop
Dunelm Technology
Published in
6 min readJan 26, 2022

It’s never been easier to write typescript lambdas thanks to the @aws-cdk/aws-lambda-nodejs package, which uses esbuild under the hood to automatically bundle your typescript into javascript! 🛠

To demonstrate this, I’m going to use the AWS CDK to deploy a simple GraphQL lambda written in typescript to AWS, with very minimal config! All you have to do is pass the path to the handler of your lambda and the NodejsFunction will do the rest!

Table of Contents:

  1. Project Setup
  2. Building the GraphQL lambda
  3. Building AWS resources with the CDK
  4. Deploying to AWS with the CDK
  5. Bonus! Running the stack locally
  6. Wrap up

Project Setup

This tutorial was written using node v14.14. You can use the Node Version Manager to make sure you’re using the right node version.

Begin by opening a terminal, creating a folder for the project and initialising an npm project using yarn init.

mkdir aws-graphql-cdk && cd "$_" && yarn init -y

Next, in order to write our code in typescript, we need to create a tsconfig.json . We are going to use tsconfig.json to truly take this project to the moon 🚀.

Run npx tsconfig.json and select ‘node’ from the technology list.

Finally, add the packages for typescript.

yarn add typescript @types/node

Now we’re ready to start coding! 💻

Building the GraphQL Lambda

First, install the apollo-server-lambda and graphql packages to create our GraphQL server with.

yarn add apollo-server-lambda graphql

Next, add a new file in src/graphql-lambda/index.ts and create a GraphQL server. This is using ApolloServer to create a simple server that exposes a ‘hello’ query. Then finally we export the lambda handler as the AWS specific integration exposed on the ApolloServer.

import { ApolloServer, gql } from 'apollo-server-lambda'const typeDefs = gql`
type Query {
hello: String
}
`
const resolvers = {
Query: {
hello: () => 'Hello from the CDK and typescript lambda!'
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: true,
})
export const handler = server.createHandler()

Building AWS Resources with the CDK

First, install the core, apigateway and lambda aws cdk packages.

yarn add @aws-cdk/core @aws-cdk/aws-apigateway @aws-cdk/aws-lambda-nodejs

Then, create a cloudformation stack to house all of the resources. Add the following code to src/cdk/stack.ts to setup an empty stack.

import { App, Construct, Stack } from '@aws-cdk/core'const serverlessGraphQLStack = (construct: Construct) => {
const stack = new Stack(construct, 'ServerlessGraphQLStack')
}
const app = new App()
serverlessGraphQLStack(app)

Now we’re ready to start adding the resources!

Add the following files for the lambda and api gateway.

src/cdk/lambda.ts

import { Runtime } from '@aws-cdk/aws-lambda'
import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs'
import { Construct } from '@aws-cdk/core'
import path from 'path'
export const nodeJSLambda = (stack: Construct, id: string, codePath: string) => {
return new NodejsFunction(stack, id, {
entry: path.join(__dirname, codePath),
runtime: Runtime.NODEJS_14_X,
bundling: {
externalModules: ['aws-sdk']
}
})
}

This function returns the NodejsFunction construct. We pass in a path to where the handler is, and set some defaults like the node version. The externalModules property allows you to specify modules that are already available in the runtime to avoid unnecessary duplication of modules in the bundle. As this is a lambda, the aws-sdk module is already available.

src/cdk/restApi.ts

import { RestApi, LambdaIntegration } from '@aws-cdk/aws-apigateway'
import { IFunction } from '@aws-cdk/aws-lambda'
import { Construct } from '@aws-cdk/core'
export const restApi = (stack: Construct, id: string) => {
return new RestApi(stack, id, {
restApiName: id
})
}
export const lambdaIntegration = (handler: IFunction) => new LambdaIntegration(handler)

This exposes two functions; one returns the RestApi construct and the other returns a LambdaIntegration construct to connect the lambda to the api.

To add these to the stack it’s a simple case of importing and invoking them!

import { App, Construct, Stack } from '@aws-cdk/core'
import { nodeJSLambda } from './lambda'
import { restApi, lambdaIntegration } from './restApi'
const serverlessGraphQLStack = (construct: Construct) => {
const stack = new Stack(construct, 'ServerlessGraphQLStack')

const graphQLServerLambda = nodeJSLambda(stack, 'GraphQLServerLambda', '../graphql-lambda/index.ts')
const graphQLServerRestApi = restApi(stack, 'GraphQLServerRestApi') const graphQLServerLambdaIntegration = lambdaIntegration(graphQLServerLambda)}const app = new App()
serverlessGraphQLStack(app)

Next, we need to integrate the lambda with the api gateway so that we can invoke it over a /graphql endpoint.

Add a resource to the api, and then add a GET and POST method, passing in the integration defined earlier.

import { App, Construct, Stack } from '@aws-cdk/core'
import { nodeJSLambda } from './lambda'
import { restApi, lambdaIntegration } from './restApi'
const serverlessGraphQLStack = (construct: Construct) => {
const stack = new Stack(construct, 'ServerlessGraphQLStack')

const graphQLServerLambda = nodeJSLambda(stack, 'GraphQLServerLambda', '../graphql-lambda/index.ts')
const graphQLServerRestApi = restApi(stack, 'GraphQLServerRestApi') const graphQLServerLambdaIntegration = lambdaIntegration(graphQLServerLambda) const graphQL = graphQLServerRestApi.root.addResource('graphql')
graphQL.addMethod('GET', graphQLServerLambdaIntegration)
graphQL.addMethod('POST', graphQLServerLambdaIntegration)
}const app = new App()
serverlessGraphQLStack(app)

Finally we need to create a cdk.json config file to tell the cdk where our app lives!

{
"app": "npx ts-node --prefer-ts-exts ./src/cdk/stack.ts"
}

That’s the stack complete, now we’re ready to deploy!

Deploying to AWS with the CDK

In order to deploy to AWS via the CDK we need the following tools installed:

(This tutorial used v1.119 of aws-cdk and 2.2.20 of the aws-cli)

Once they are installed, make sure you have also configured your AWS environment in the aws-cli by running aws configure and entering your access keys.

The next step is to bootstrap the AWS account being used, which will automatically provision resources the CDK needs to deploy the stack.

cdk bootstrap aws://<aws_account_id>/<aws_region>

E.g. cdk bootstrap aws://03057671267/us-east-1

Finally, add a script to the package.json that can be run to deploy the stack.

{  
"name": "aws-graphql-cdk",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@aws-cdk/aws-apigateway": "^1.119.0",
"@aws-cdk/aws-lambda-nodejs": "^1.119.0",
"@aws-cdk/core": "^1.119.0",
"@types/node": "^16.6.2",
"apollo-server-lambda": "^3.1.2",
"graphql": "^15.5.1",
"typescript": "^4.3.5"
},
"scripts": {
"cdk": "cdk synth && cdk deploy --require-approval never",
}
}

This will synthesise your CDK code into a CloudFormation template and then deploy it, authorising the changes.

Now comes the exciting part! Run yarn cdk and watch the magic happen. Once it’s finished deploying, you will get an output on the terminal of the endpoint to invoke the api gateway on.

CDK deploy output

You can test it out in Postman by sending a POST request to https://<your_endpoint>/graphql with the following body:

query {
hello
}

E.g. https://mu1iuirj4j.execute-api.us-east-1.amazonaws.com/prod/grahpql

You should receive a successful response back ✅

{
"data": {
"hello": "Hello from the CDK and typescript lambda!"
}
}

Bonus! Running the stack locally

It’s possible to run the stack locally using sam-beta-cdk if you don’t have access to an AWS environment whilst developing. To install it, run the following command: (you’ll need homebrew 🍺 for this, and you may need to run brew tap aws/tap beforehand)

brew install aws-sam-cli-beta-cdk

Then add another script to your package.json to run it locally.

{  
"name": "aws-graphql-cdk",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@aws-cdk/aws-apigateway": "^1.119.0",
"@aws-cdk/aws-lambda-nodejs": "^1.119.0",
"@aws-cdk/core": "^1.119.0",
"@types/node": "^16.6.2",
"apollo-server-lambda": "^3.1.2",
"graphql": "^15.5.1",
"typescript": "^4.3.5"
},
"scripts": {
"cdk": "cdk synth && cdk deploy --require-approval never",
"dev": "cdk synth && sam-beta-cdk local start-api"
}
}

This will again synthesise your cdk code into a CloudFormation template and then run it locally using sam-beta-cdk.

Before running it locally, you will need docker 🐳 installed and running.

Run yarn dev and then you can test it out with Postman by sending the same POST request as earlier, just swapping out your AWS url with the url output in the terminal, normally http://localhost:3000/graphql .

You should receive the same successful response back ✅

{
"data": {
"hello": "Hello from the CDK and typescript lambda!"
}
}

Wrap up

That’s it! The CDK constructs used in this article take away a lot of the hassle in deploying your code to AWS, and they’re available for other languages such as Python and Go!

Happy building! 🏠

Check out the sample repo.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Published in Dunelm Technology

Tales about the great technology being used and the talented team behind it at Dunelm — the home of homes. Covering everything from high level whim-driven musings about technological trends right through to deep-dive technical discussions and personal projects.

No responses yet

What are your thoughts?