🍰 Build & Deploy Reusable Lambda Layers Using AWS CDK

Mithun Das
5 min readOct 7

What is a lambda layer?

A Lambda layer is a ZIP archive that contains additional code and data that can be referenced by AWS Lambda functions. Lambda layers allow you to pull in shared code, assets, and dependencies to be used across multiple functions, without needing to include it in every Lambda deployment package. AWS introduced lambda layer in 2018, few years after they launched lambda in 2014.

Let me explain with an example. If you are coming from commonly used spring boot microservices or ASP.NET or Express.js backend, you might be familiar with the Data Access Object (DAO) pattern to abstract and encapsulate data access and persistence logic from the business logic. We typically write some common classes with CRUD methods and reuse those classes across the application.

Usually lambdas are single-purpose meaning it performs one small task such as “add a todo”, “view all todos” etc. And you need to copy/paste your DAO in every lambda function which will become maintenance nightmare very soon. What if you could package your common codes somewhere, seperate from your lambda code and reuse them at runtime? This is exatcly what lambda layer gives you along with many other benefits

  • Layers can be shared across accounts and regions. They simplify deployments especially when multiple functions use the same dependencies.
  • Layers keep deployment packages small and organized.

What will we build today?

We will continue from our last article, getting started with aws cdk , add a lambda layer which interacts with dynamodb, add another lambda and api route.


  • Working CDK. Follow my last article to set it up.

Create dynamoDB

As we are going to create a lambda layer to encapsulate all the dynamodb related codes such as get, put, delete etc, let’s first create the table. For this application we will create only one table inspired by “single table design pattern”. If you are not familiar with it, I highly recommend you to read Alex DeBrie’s post.

🍅 Tip — if you are familiar with MySQL Workbench then you will like NoSQL Workbench. Try installing and get familiar.

Long story short, let’s create one table with a primary key and a short key and one global secondary index, again with a primary key and a short key. GSI will not be relevant in this article but you will find the use of that in my future article when we expand the scope of this application.

const mainTable = new dynamodb.Table(this, "todo-single-table", {
partitionKey: {
name: "PK",
type: dynamodb.AttributeType.STRING,
sortKey: {
name: "SK",
type: dynamodb.AttributeType.STRING,
timeToLiveAttribute: "ttl",
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
removalPolicy: cdk.RemovalPolicy.DESTROY,
encryption: dynamodb.TableEncryption.CUSTOMER_MANAGED,
encryptionKey: encryptionKey,

indexName: "GSI1",
partitionKey: {
name: "GSI1PK",
type: dynamodb.AttributeType.STRING,
sortKey: {
name: "GSI1SK",
type: dynamodb.AttributeType.STRING,

Create the layer

Creating layer using CDK is super straight forward and just few lines of code.

    const dbHelperLayer = new lambda.LayerVersion(this, "db-helper-layer", {
compatibleRuntimes: [lambda.Runtime.NODEJS_18_X],
code: lambda.Code.fromAsset("lambda/db-helper-layer"),
description: "Dynamo related commmon code and access patterns",

Take a look at the folder structure which is a bit different from regular lambda. Notice that data-helper.js is under “nodejs” folder.

During runtime AWS will mount nodejs folder under /opt and you should be able to access data-helper.js as

const dbhelper = require("/opt/nodejs/data-helper");

Let’s add some code to the layer

const addTodo = async (email, todo, timestamp, ttlInDays) => {
const input = {
PK: `USER#${email}`,
SK: `TODO#${timestamp}`,
GSI1PK: `USER#${email}`,
GSI1SK: `TODO#${timestamp}`,
EntityType: "TODO",
Checked: false,
Todo: todo,
TTL: new Date().getTime() / 1000 + 3600 * 24 * ttlInDays,

await putItemInDB(MAIN_TABLE_NAME, input);

const getTodoByUserid = async (email) => {
try {
const command = new QueryCommand({
KeyConditionExpression: "PK = :PK AND begins_with(SK, :SK) ",
ExpressionAttributeValues: {
":PK": `USER#${email}`,
":SK": `TODO#`,
ConsistentRead: false,
ReturnConsumedCapacity: "INDEXES",

const response = await docClient.send(command);
console.log("Consumed capacity:", response.ConsumedCapacity);
console.log("Count:", response.Count);
const runtimes = response.Items;

return runtimes;
} catch (error) {
throw error;

At this point, you should be able to deploy your stack which will create the lambda layer.

Reuse the layer

Let’s create two lambda functions and attach the layer.

    const getTodoLambda = new lambdanodejs.NodejsFunction(this, "getTodoLambda", {
runtime: lambda.Runtime.NODEJS_18_X,
timeout: cdk.Duration.seconds(5),
entry: "lambda/api-get-todo/index.js",
handler: "handler",
environment: {
+ TABLE_MAIN: mainTable.tableName,
bundling: {
nodeModules: [],
- externalModules:[],
+ externalModules: ["/opt/nodejs/data-helper"],
- layers: [],
+ layers: [dbHelperLayer],
+ mainTable.grantReadData(getTodoLambda);

Another beauty of CDK is — how easy it is to apply fine grained permissions to your resources.

Redeploy the code and test both APIs


🍎 Find the source code on my github

🌟 Coming up…

Consider subscribing and stay tuned for a series of upcoming articles dedicated to AWS CDK, where we’ll dive deeper into its features, share practical tips, and explore real-world use cases, helping you master the art of Infrastructure as Code.

  • Distributed map state function using CDK
  • Send emails using SES email templates and CDK
  • Openserarch serverless using CDK
  • Lambda Edge With CDK
  • Github Action With CDK

If you are interested in other topics, let me know in the comment section.

Mithun Das

Sr Principal Engineer | Tech Enthusiast | AWS Certified Solutions Architect | IoT Evangelist