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:
        ./node_modules/.bin/esbuild utils.ts --bundle --platform=node --target=node22 --outfile=$(ARTIFACTS_DIR)/nodejs/index.js

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") })
  };
};

🌟 BONUS: Using Powertools Logger as a Global Layer

If you want structured logging, tracing, and metrics — use AWS Lambda Powertools for TypeScript as a global layer. AWS provides it as a public layer:

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs18.x
      Layers:
        - !Sub 
arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:24
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: "es2020"
        Sourcemap: false
        EntryPoints:
          - index.ts
        External:
          - "@aws-lambda-powertools/*"
          - "@aws-sdk/*"

or globally:

Globals:
  Function:
    Layers:
      - !Sub arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:24

Example Use

import { Logger } from "@aws-lambda-powertools/logger";

const logger = new Logger({ serviceName: "MyApp" });

export const handler = async () => {
  logger.info("Lambda invoked");
  return {
    statusCode: 200,
    body: JSON.stringify({ message: "Logged with Powertools" })
  };
};

Conclusion

You now know how to:

  • Create a reusable Lambda Layer with esbuild

  • Structure a Makefile build process for SAM

  • Use AWS Lambda Powertools for structured logging