Skip to content

Compatibility

Eugene Lazutkin edited this page Apr 23, 2026 · 2 revisions

Compatibility

AWS Lambda runtimes

The adapter targets AWS Lambda's Node runtime. It uses no Node-version-specific features beyond native ESM and the bundled node:buffer module (imported only by read-lambda-body.js and local.js — the main handler has no node:* imports).

Runtime Status Notes
nodejs20.x LTS, default target. engines.node: '>=20'.
nodejs22.x Current LTS at time of writing.
nodejs18.x End-of-life on AWS Lambda. Won't run — engines.node blocks the install.
Lambda container images Any base image on the supported Node runtime list.

For non-Lambda hosting (testing, local dev), the local-debug bridges expand the surface — see below.

Event shapes

All four supported via a single auto-detecting factory:

Trigger Payload format Result envelope
API Gateway REST API 1.0 APIGatewayProxyResult
API Gateway HTTP API 2.0 APIGatewayProxyStructuredResultV2
Lambda Function URL 2.0 APIGatewayProxyStructuredResultV2
Application Load Balancer 1.0-ish ALBResult (+ multiValueHeaders on ALB multi-value mode)

See Event shapes for the detection rules and per-shape gotchas.

No framework peer dependency

Unlike dynamodb-toolkit-koa, dynamodb-toolkit-express, or dynamodb-toolkit-fetch, this package has no framework peer. @types/aws-lambda is a dev-only type dependency; the production runtime is AWS Lambda's Node runtime, which is ambient on the target platform.

peerDependencies:

{
  "peerDependencies": {
    "dynamodb-toolkit": "^3.7.0"
  }
}

Local-debug bridge runtimes

The bridges in dynamodb-toolkit-lambda/local.js let you run the Lambda handler under any of these runtimes without deploying:

Runtime Bridge Notes
Node 20+ createNodeListener http.createServer(listener).listen(port).
Bun createFetchBridge Bun.serve({port, fetch: bridge}).
Deno createFetchBridge Deno.serve({port}, bridge).
Cloudflare Workers createFetchBridge export default {fetch: bridge}.
Hono / itty-router createFetchBridge Pass the raw Request.
Koa / Express (as glue) createNodeListener 10 lines of glue; see Local debug bridges.

The Node bridge depends on node:http + node:buffer; the Fetch bridge depends on node:buffer only (used for base64 ↔ bytes). Bun and Deno ship node:buffer as a compat shim; Cloudflare Workers ship it as part of the nodejs_compat flag.

Cross-runtime test matrix

The adapter's unit test suite runs identically under Node / Bun / Deno via the local-debug bridges:

Runtime Command Tests / Asserts Notes
Node npm test 84 / 174 Includes a .cjs smoke (Node-only)
Bun npm run test:bun 79 / 169 Same suite minus the .cjs smoke
Deno npm run test:deno 79 / 169 Same

The 5-test delta is the .cjs smoke scoped to Node via tape6's node config key — CommonJS-from-ESM-sibling semantics differ between Bun and Deno, so it doesn't run there.

TypeScript smoke (npm run ts-test / ts-test:bun / ts-test:deno): identical counts on all three.

Node version floor

engines.node is >=20. Two sub-floors worth knowing:

  • require(esm) interop for CJS consumers — 20.19+ on the 20.x line, 22.12+ on 22.x, unflagged everywhere newer. The .cjs smoke test exercises this.
  • Native TypeScript — Node 22.6+ (with --experimental-strip-types) or 23.6+ (unflagged). Used by npm run ts-test. Bun and Deno run .ts natively without special setup.

Both smoke-tests are included so you can verify your target runtime handles the adapter correctly.

AWS Lambda's nodejs20.x runtime image ships Node 20.15 at the time of writing; .cjs interop and native TS are not available there. Use ESM for handler modules (the "type": "module" flag in package.json) and compile TypeScript ahead of deployment.

CommonJS consumers

require('dynamodb-toolkit-lambda') works on Nodes that ship require(esm) (20.19+ / 22.12+ / anything newer):

const {createLambdaAdapter} = require('dynamodb-toolkit-lambda');
const {createNodeListener} = require('dynamodb-toolkit-lambda/local.js');

AWS Lambda's nodejs20.x image ships 20.15, which predates require(esm). CJS handlers on Lambda need to either:

  • Wait for the Lambda runtime to advance to 20.19+ (rolling release), or
  • Use an ESM handler module ("type": "module" + import).

ESM is the recommended default on Lambda — the AWS CLI / IaC tooling supports it natively.

TypeScript consumers

Hand-written .d.ts sidecars ship next to each .js file. package.json declares main, module, and types for all resolution styles:

{
  "main": "./src/index.js",
  "module": "./src/index.js",
  "types": "./src/index.d.ts",
  "exports": {
    ".": "./src/index.js",
    "./*": "./src/*"
  }
}

The factory is generic in TItem so typed Adapter<Item, Key> instances flow through without casts:

import {Adapter} from 'dynamodb-toolkit';
import {createLambdaAdapter, type LambdaAdapterOptions} from 'dynamodb-toolkit-lambda';

interface Planet extends Record<string, unknown> {
  name: string;
  climate?: string;
}
type PlanetKey = Pick<Planet, 'name'>;

const adapter = new Adapter<Planet, PlanetKey>({client, table: 'planets', keyFields: ['name']});

const opts: LambdaAdapterOptions<Planet> = {
  mountPath: '/planets',
  keyFromPath: (raw, a) => ({[a.keyFields[0].name]: raw}),
  policy: {defaultLimit: 25, maxLimit: 200}
};
export const handler = createLambdaAdapter(adapter, opts);

See tests/test-typed.ts in the source tree for a runnable typed example.

AWS types

The adapter imports from aws-lambda (namespace @types/aws-lambda) for event / result / context types. @types/aws-lambda is a dev-only type dependency — nothing at runtime depends on it. If your TypeScript config is strict and you don't explicitly add it to your project, you'll get a "cannot find module 'aws-lambda'" error — install it yourself:

npm install --save-dev @types/aws-lambda

Callers who don't care about the precise event type can use the re-exported LambdaEvent / LambdaResult aliases from this package, which expand to the same union:

import type {LambdaEvent, LambdaResult} from 'dynamodb-toolkit-lambda';

const myWrapper = async (event: LambdaEvent): Promise<LambdaResult> => /* ... */;

Package shape

  • ESM-only source. .js + hand-written .d.ts, no build step. Runs on Lambda via the native ESM loader ("type": "module").
  • Zero runtime dependencies. dynamodb-toolkit is a peer dep; no framework peer.
  • node:buffer is the only node:* import at runtime, and only inside read-lambda-body.js + local.js. The main index.js has no Node-specific imports — it runs verbatim on any runtime with a reasonable Buffer shim, which covers every Lambda-compatible environment.

Tested against

The adapter's own test matrix exercises the full route pack:

  • 79 tests, 169 assertions under Bun / Deno (via the local-debug bridges).
  • 84 tests, 174 assertions under Node (adds a .cjs require-interop smoke).
  • TypeScript smoke tests via ts-test — runs on Node (native .ts support on 22.6+), Bun, and Deno.

No sam local integration test is wired into CI — the local-debug bridges give equivalent coverage with no Docker dependency. If you need sam local confirmation for a specific trigger config (e.g. a custom authorizer), exercise it manually against the dev bridge first, then sam-local only for the trigger-side verification.

Clone this wiki locally