Creating and Using Lambda Layers with AWS SAM and Node.js

As your Lambda functions grow, you’ll want to avoid duplicating code or dependencies across functions. AWS Lambda Layers solve this by letting you package shared code (like libraries or utilities) once and reuse them across multiple Lambdas. In this post, you’ll learn: How to create a custom Lambda Layer with AWS SAM using Node.js How to bundle it with esbuild using a Makefile How to use AWS Lambda Powertools for TypeScript as a global logging layer What is a Lambda Layer? A Lambda Layer is a ZIP archive containing libraries, a runtime, or other dependencies. It’s extracted to the /opt directory in the Lambda runtime, making it perfect for sharing utility code or dependencies across functions. Step 1: Create a Layer in Your SAM Project Let’s create a utility layer (e.g., utils) to share code. Directory structure: my-sam-app/ ├── layers/ │ └── utils/ │ └── utils.js │ └── package.json │ └── tsconfig.json │ └── Makefile Your utils.js could be something like: export const greet = (name: string) => { return `Hello, ${name}!`; }; Step 2: Use a Makefile with esbuild The tsconfig.json: { "compilerOptions": { "strict": true, "target": "es2023", "preserveConstEnums": true, "resolveJsonModule": true, "noEmit": false, "sourceMap": false, "module": "commonjs", "moduleResolution": "node", "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "outDir": "./utils" }, "exclude": ["node_modules", "**/*.test.ts"] } Create a Makefile inside the utils/ folder: build-Utils: npm install npm run build mkdir -p "$(ARTIFACTS_DIR)/nodejs/node_modules" cp package.json package-lock.json "$(ARTIFACTS_DIR)/nodejs/" # for runtime deps npm install --omit=dev --prefix "$(ARTIFACTS_DIR)/nodejs/" # for runtime deps rm "$(ARTIFACTS_DIR)/nodejs/package.json" # for runtime deps cp -r utils "$(ARTIFACTS_DIR)/nodejs/node_modules" SAM will automatically pick this up when you run sam build. Step 3: Define the Layer in template.yml Resources: UtilsLayer: Type: AWS::Serverless::LayerVersion Properties: LayerName: utils-layer Description: Common utility functions ContentUri: layers/utils CompatibleRuntimes: - nodejs22.x RetentionPolicy: Retain Metadata: BuildMethod: makefile Step 3: Attach Layer to Lambda Function Now add it to your Lambda function: Resources: HelloWorldFunction: Type: AWS::Serverless::Function Properties: Handler: index.handler Runtime: nodejs22.x Layers: - !Ref UtilsLayer Metadata: BuildMethod: esbuild BuildProperties: Minify: true Target: "es2020" Sourcemap: false EntryPoints: - index.ts External: - utils - "@aws-sdk/*" Step 4: Use Layer in Lambda Code In your index.js or index.ts: import { greet } from 'utils'; export const handler = async () => { return { statusCode: 200, body: JSON.stringify({ message: greet("SAM Developer") }) }; };

Apr 10, 2025 - 18:10
 0
Creating and Using Lambda Layers with AWS SAM and Node.js

As your Lambda functions grow, you’ll want to avoid duplicating code or dependencies across functions. AWS Lambda Layers solve this by letting you package shared code (like libraries or utilities) once and reuse them across multiple Lambdas.

In this post, you’ll learn:

  • How to create a custom Lambda Layer with AWS SAM using Node.js

  • How to bundle it with esbuild using a Makefile

  • How to use AWS Lambda Powertools for TypeScript as a global logging layer

What is a Lambda Layer?

A Lambda Layer is a ZIP archive containing libraries, a runtime, or other dependencies. It’s extracted to the /opt directory in the Lambda runtime, making it perfect for sharing utility code or dependencies across functions.

Step 1: Create a Layer in Your SAM Project

Let’s create a utility layer (e.g., utils) to share code.

Directory structure:

my-sam-app/
├── layers/
   └── utils/
       └── utils.js
       └── package.json
       └── tsconfig.json
       └── Makefile

Your utils.js could be something like:

export const greet = (name: string) => {
  return `Hello, ${name}!`;
};

Step 2: Use a Makefile with esbuild

The tsconfig.json:

{
  "compilerOptions": {
    "strict": true,
    "target": "es2023",
    "preserveConstEnums": true,
    "resolveJsonModule": true,
    "noEmit": false,
    "sourceMap": false,
    "module": "commonjs",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./utils"
  },
  "exclude": ["node_modules", "**/*.test.ts"]
}

Create a Makefile inside the utils/ folder:

build-Utils:
    npm install
    npm run build
    mkdir -p "$(ARTIFACTS_DIR)/nodejs/node_modules"
    cp package.json package-lock.json "$(ARTIFACTS_DIR)/nodejs/" # for runtime deps
    npm install --omit=dev --prefix "$(ARTIFACTS_DIR)/nodejs/" # for runtime deps
    rm "$(ARTIFACTS_DIR)/nodejs/package.json" # for runtime deps
    cp -r utils "$(ARTIFACTS_DIR)/nodejs/node_modules"

SAM will automatically pick this up when you run sam build.

Step 3: Define the Layer in template.yml

Resources:
  UtilsLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: utils-layer
      Description: Common utility functions
      ContentUri: layers/utils
      CompatibleRuntimes:
        - nodejs22.x
      RetentionPolicy: Retain
      Metadata:
        BuildMethod: makefile

Step 3: Attach Layer to Lambda Function

Now add it to your Lambda function:

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs22.x
      Layers:
        - !Ref UtilsLayer
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: "es2020"
        Sourcemap: false
        EntryPoints:
          - index.ts
        External:
          - utils
          - "@aws-sdk/*"

Step 4: Use Layer in Lambda Code

In your index.js or index.ts:

import { greet } from 'utils';

export const handler = async () => {
  return {
    statusCode: 200,
    body: JSON.stringify({ message: greet("SAM Developer") })
  };
};