Skip to main content

The SDK version bundled with Lambda

AWS Lambda Node.js runtimes include a specific minor version of the AWS SDK — not the latest. To see which version is included with your runtime, check the runtime-included SDK versions in the Lambda Developer Guide.
Do not rely on the Lambda-provided SDK for production functions. The bundled version is pinned and will not receive updates. Bundle the SDK with your function code or use a Lambda layer to control the exact version you depend on.
To discover the installed version at runtime:
const pkgJson = require("@aws-sdk/client-s3/package.json");

exports.handler = function (event) {
  console.log(pkgJson.version);
  return JSON.stringify(pkgJson);
};

Credentials in Lambda

You do not need to provide explicit AWS credentials in Lambda. The SDK automatically uses the function’s execution role via instance metadata (IMDS). Pass an empty config object to the client constructor:
import { S3Client } from "@aws-sdk/client-s3";

const client = new S3Client({}); // credentials resolved from execution role

Client initialization best practices

1

Initialize clients outside the handler

Create SDK clients at module scope, outside the handler function. Lambda reuses the same container across invocations (container reuse / warm starts), so the client is initialized once and reused.
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";

// Initialized once per container lifecycle
const client = new STSClient({});

export const handler = async (event) => {
  const results = await client.send(new GetCallerIdentityCommand({}));
  return {
    statusCode: 200,
    body: JSON.stringify({ userId: results.UserId }),
  };
};
2

Make API calls inside the handler

Always invoke client.send(...) from within the handler, not at module scope. This ensures requests are signed at execution time, after the container is “hot”, avoiding signing time skew.

One-time setup inside the handler

If your initialization involves async work (fetching secrets, assuming a role), you may be tempted to run it at module scope:
// Risky: network requests made outside the handler may be frozen mid-flight
// during provisioned concurrency pre-warming
const ready = prepare();

export async function handler(event) {
  await ready;
  // ...
}
This is risky. Lambda’s provisioned concurrency can freeze the execution context between the module initialization and actual invocation. Time-sensitive signed requests made during prepare() may expire before the function is invoked. Recommended pattern: run setup inside the handler, but only once:
let ready = false;

export async function handler(event) {
  if (!ready) {
    await prepare();
    ready = true;
  }
  // ...
}
A practical example with lazy client initialization:
import { fromTemporaryCredentials } from "@aws-sdk/credential-providers";
import { DynamoDB } from "@aws-sdk/client-dynamodb";

async function prepare() {
  const credentials = fromTemporaryCredentials({})();
  return new DynamoDB({ credentials });
}

let client: DynamoDB | null = null;

export async function handler(event) {
  if (!client) client = await prepare();

  return client.getItem({
    TableName: "my-table",
    Key: { id: { S: "1" } },
  });
}

Creating a Lambda layer for the SDK

Using a Lambda layer lets multiple functions share the same SDK version without bundling it in each deployment package. 1. Create the layer content In a clean directory, install the SDK packages you need:
package.json
{
  "dependencies": {
    "@aws-sdk/client-s3": "<=3.750.0",
    "@aws-sdk/client-dynamodb": "<=3.750.0",
    "@aws-sdk/lib-dynamodb": "<=3.750.0",
    "@aws-sdk/client-lambda": "<=3.750.0"
  }
}
Run npm install, then zip the node_modules folder into the required structure:
layer_content.zip
└── nodejs/
    └── node_modules/
        └── @aws-sdk/
2. Publish the layer
import { Lambda } from "@aws-sdk/client-lambda";
import fs from "node:fs";

const lambda = new Lambda();

await lambda.publishLayerVersion({
  LayerName: "AWS-SDK-JavaScript-v3-layer",
  Description: "AWS SDK v3 dependencies",
  Content: {
    ZipFile: fs.readFileSync("./layer_content.zip"),
  },
  CompatibleRuntimes: ["nodejs18.x", "nodejs20.x", "nodejs22.x"],
  CompatibleArchitectures: ["x86_64", "arm64"],
});
3. Attach the layer to a function
import { Lambda } from "@aws-sdk/client-lambda";

const lambda = new Lambda();

const { Layers } = await lambda.listLayers({});
const layerArn = Layers.find(
  (l) => l.LayerName === "AWS-SDK-JavaScript-v3-layer"
).LatestMatchingVersion.LayerVersionArn;

await lambda.updateFunctionConfiguration({
  FunctionName: "MY_FUNCTION",
  Layers: [layerArn],
});

Performance

Bundling your Lambda function with esbuild or a similar tool significantly reduces cold start time. A bundled v3 application on Node.js 18+ has measurably faster cold starts than v2. See the AWS blog post on reducing Lambda cold start times for benchmarks. For parallel request workloads inside Lambda handlers, see Performance.

Build docs developers (and LLMs) love