How to reference Lambda Layers in imports using AWS Lambda with Typescript

Using Lambda Layers and Typescript are great ways to improve your development experience when working with AWS Lambda functions, however, you might run into an annoying issue that at first (for me, at least) doesn't seem to have an obvious solution: how to reference layers in your Typescript import statements.

Creating an AWS Lambda Function

Let's start off with a simple Typescript CDK project with one AWS Lambda function. When using AWS CDK or AWS SAM, I like to create a src/ directory for my actual code. Inside the src/ directory, let's create a new folder for our function, called testLambda/, and an index.ts file to put our lambda's code.

-src/
  -testLambda/
    -index.ts
export const handler =  async (event: any) => {

    const response = {
        statusCode: 200,
        body: "Hello World"
    }

    return response;
  }

Creating a layer

Now, let's say that instead of "Hello World", we want to return just the first character in the string. This may be a common functionality that we may want to implement in many lambdas, so we decide to put it into a layer.

Let's create a separate directory inside our src/ directory, testLayer/, where we will store out layer's code, and then create a file utils.ts, where our utility functions will live.

-src/
  -testLambda/
    -index.ts
  -testLayer/
    -utils.ts

This is our utils file

export const getFirstLetter = (inputString: string): string => {
    return inputString.charAt(0);
}

Usually, we would have to import our getFirstLetter function following our directory structure like this:

import { getFirstLetter } from "../testLayer/nodejs/utils"

However, lambda layers have to be imported into functions with a specific path depending on what runtime you use. In the case of Node, custom code in lambda layers is stored in the /opt/nodejs/ path.

We are required to import the function using the previous path:

import { getFirstLetter } from "/opt/nodejs/utils"

After importing the function and using it to get our first letter out of "Hello World", our whole index.ts file looks like this so far:

import { getFirstLetter } from "/opt/nodejs/utils"

export const handler =  async (event: any) => {

    const response = {
        statusCode: 200,
        body: getFirstLetter("Hello World")
    }

    return response;
  }

Although this is technically correct, Typescript doesn't like it when it can't find the file referenced in the import statement, thus, if we want to compile the project with tsc, we will get this error:

src/testLambda/index.ts:1:32 - error TS2307: Cannot find module 'opt/nodejs/utils' or its corresponding type declarations.

This happens because we're not actually keeping this directory in our development machine. Luckily, to fix this issue involves only a small change in the Typescript configuration file for something called path mapping.

We must add the nodes "baseUrl" and "paths" to the tsconfig.json file, where we can specify the base directory of our project, relative to tsconfig.json, and any path mappings, respectively:

    "baseUrl": ".",
    "paths": {
      "/opt/nodejs/*": ["src/testLayer/nodejs/*"]
    }

What this does, is tell the Typescript compiler that the files in /opt/nodejs/ are really in /src/testLayer/nodejs, so the error when compiling goes away and you can keep enjoying all the benefits of Typescript and layers along with AWS Lambda.

You can find the code for this guide in this repository github.com/VictorPeralta/lambda-layers-type..