Skip to content

Migration: v2 to v3

Eugene Lazutkin edited this page Apr 23, 2026 · 1 revision

Migration: v2 → v3

v3 is a green-field rewrite. v2 consumers stay on v2 (dynamodb-toolkit@2.3.0 is preserved on npm; v2 wiki lives at the v2.3-docs git tag). Where you do upgrade, expect a few broad changes.

For AWS SDK-level changes (Commands, paginators, credential providers, .promise(), etc.) see SDK v2 to v3 cheat sheet.

Module shape

v2 v3
const Adapter = require('dynamodb-toolkit') import {Adapter} from 'dynamodb-toolkit'
require('dynamodb-toolkit/utils/applyBatch') import {applyBatch} from 'dynamodb-toolkit/batch'
require('dynamodb-toolkit/helpers/KoaAdapter') Koa wrapper lives in a separate package (TBA). For node:http: import {createHandler} from 'dynamodb-toolkit/handler'
CommonJS ESM only — "type": "module" in package.json

The exports map exposes seven sub-exports:

  • dynamodb-toolkitAdapter, Raw, raw, TransactionLimitExceededError
  • dynamodb-toolkit/expressions
  • dynamodb-toolkit/batch
  • dynamodb-toolkit/mass
  • dynamodb-toolkit/paths
  • dynamodb-toolkit/rest-core
  • dynamodb-toolkit/handler

Client construction

// v2
const AWS = require('aws-sdk');
AWS.config.update({region: 'us-east-1'});
const client = new AWS.DynamoDB.DocumentClient();

// v3
import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
import {DynamoDBDocumentClient} from '@aws-sdk/lib-dynamodb';

const client = new DynamoDBClient({region: 'us-east-1'});
const docClient = DynamoDBDocumentClient.from(client, {
  marshallOptions: {removeUndefinedValues: true}
});

The Adapter requires a DynamoDBDocumentClient — the AWS.DynamoDB (raw) client is no longer supported. The removeUndefinedValues option is the v2-parity setup (v2's marshaller silently dropped undefined; v3 throws by default).

makeClient / createClient / getProfileName are gone. Use @aws-sdk/credential-providers (fromIni, fromNodeProviderChain, …) directly.

One data format

v2 had three data shapes (user-land JSON, Adapter.Raw for DocumentClient-style, Adapter.DbRaw for raw DynamoDB). v3 collapses to one shape: plain JS, marshalled by lib-dynamodb middleware.

v2 v3
Adapter.Raw / Adapter.DbRaw single Raw<T> class (see Adapter: Raw marker)
Adapter.markAsRaw(item) raw(item)
returnRaw: 'raw' / 'db-raw' {reviveItems: false}
specialTypes: {field: 1} Use new Set(...) directly — lib-dynamodb recognizes Sets natively
converter / converterOptions Construct your own DynamoDBDocumentClient with marshallOptions / unmarshallOptions and pass it as client
adapter.fromDynamoRaw(item) / toDynamoRaw(item) Removed — items are always plain JS
adapter.convertFrom(item) / convertTo(item) Removed — lib-dynamodb middleware handles it

Options bags

v2 used positional flags (force, returnRaw, ignoreIndirection, …); v3 uses options objects.

v2 v3
adapter.put(item, true, params) adapter.put(item, {force: true, params})
adapter.patch({...item, __delete: ['x']}) adapter.patch(key, patch, {delete: ['x'], separator: '.'})
adapter.cloneByKey(key, mapFn, true) adapter.clone(key, mapFn, {force: true})
adapter.getByKey(key, fields, params, true) adapter.getByKey(key, fields, {reviveItems: false, params})

The getByKey method now takes a key (not a full item). v2's adapter.get(item) was equivalent to adapter.getByKey(item); in v3 there is no get — use adapter.getByKey(key).

Hook renames

v2 v3
prepareListParams(item, index) prepareListInput(example, index)
updateParams(params, options) updateInput(input, op) — same role, renamed for v3-Command-input parity

User overrides now go in a hooks bag:

// v2
new Adapter({
  client, table, keyFields,
  prepare(item) { ... },
  revive(item) { ... }
});

// v3
new Adapter({
  client, table, keyFields,
  hooks: {
    prepare(item) { ... },
    revive(item) { ... }
  }
});

(Subclassing the Adapter still works.)

Patch shape

// v2 — magic keys on the patch object
adapter.patch({name: 'X', diameter: 13000, __delete: ['climate'], __separator: '.'});

// v3 — explicit options bag
adapter.patch({name: 'X'}, {diameter: 13000}, {delete: ['climate'], separator: '.'});

On the wire, the v3 REST handler accepts _delete / _separator / _arrayOps (single underscore by default; configurable via policy.metaPrefix). v2 used __delete / __separator (double underscore) — set policy.metaPrefix: '__' to retain that.

Mass methods

generic* siblings are folded into a strategy option:

// v2
adapter.genericPutAll(items);

// v3
adapter.putItems(items, {strategy: 'sequential'});

'native' (default) uses BatchWriteItem; 'sequential' does individual Puts.

cloneByKey / moveByKey collapse to single-item clone / move (the key is the only positional arg).

Transaction limit

DynamoDB raised TransactWriteItems to 100 actions per call (was 25). v3's TRANSACTION_LIMIT is 100. The Adapter's auto-upgrade and applyTransaction honor the new limit; throws TransactionLimitExceededError when exceeded.

REST handler

The v2 KoaAdapter is replaced by:

  • dynamodb-toolkit/rest-core — pure parsers/builders/policy, framework-agnostic. See REST core.
  • dynamodb-toolkit/handlernode:http (req, res) => adapter. See HTTP handler.

The Koa wrapper lives in a separate package (TBA). The node:http handler covers the same URL contract as v2's KoaAdapter but with single-underscore meta keys, options-bag policy, and richer error mapping (no more catch-all 500409/422/429/503 mapped per the policy's status codes).

Tests

v2 used Postman + a Koa test server. v3 uses tape-six with node:test's mock API for integration coverage and DynamoDB Local + built-in fetch for end-to-end coverage. See npm test and npm run test:e2e.

Types

v3 ships hand-written .d.ts sidecars for every module (no TypeScript build step). Generic types: Adapter<TItem, TKey = Partial<TItem>>, Raw<T>, BatchDescriptor<TItem>, PaginatedResult<T>, ListOptions, RestPolicy, …

Where to read more

Clone this wiki locally