Skip to content

Adapter: Constructor options

Eugene Lazutkin edited this page Jul 17, 2026 · 3 revisions

Adapter: Constructor options

See also (AWS JS SDK v3): DynamoDBDocumentClient.from · Global Secondary Indexes. Vocabulary: Concepts.

new Adapter<TItem, TKey = Partial<TItem>>(options: AdapterOptions<TItem, TKey>);

Required

Field Type Notes
client DynamoDBDocumentClient Construct via DynamoDBDocumentClient.from(...).
table string Base table name.
keyFields (string | KeyFieldSpec)[] Partition key first, optional sort key second. String shorthand: ['state'][{name: 'state', type: 'string'}]. For composite keys (length > 1), number components need width so zero-padding keeps the joined structural key lexicographically sortable. See Concepts: Structural key.

Technical-fields machinery (opt-in)

Field Type Notes
technicalPrefix string Marks fields as adapter-managed. Incoming user items are rejected if any field starts with this prefix; on read, such fields are stripped before the revive hook sees them. When set, adapter-managed field names (structural key, search mirrors, version, createdAt) are validated to start with this prefix. Convention: '_'.

Structural key (required when keyFields.length > 1)

Field Type Notes
structuralKey string | {name, separator?} Name of the joined sort-key attribute. String shorthand: structuralKey: '_sk' ≡ `{name: '_sk', separator: '

Type tags

Field Type Notes
typeLabels string[] Paired 1:1 with keyFields. typeLabels[i] is the label returned by adapter.typeOf(item) when the item has keyFields[0..i] defined.
typeDiscriminator string | {name} Field name whose value overrides depth-based detection in typeOf. String shorthand: typeDiscriminator: 'kind'. Useful for multi-kind leaves (cars + boats under the same keyFields).
typeField string Name of a field auto-populated by the built-in prepare step with adapter.typeOf(item). Combined with typeDiscriminator pointing at the same field, the label round-trips (read back from DynamoDB with typeOf). Enables Pattern-1 sparse-GSI recipes.

Indices

Field Type Notes
indices Record<string, IndexSpec> Declared GSI / LSI map. Each entry is discriminated by type: 'gsi' | 'lsi'. pk / sk accept string shorthand (pk: 'status') or full {name, type} descriptors. See Adapter: Indirect indices for the legacy keys-only + BatchGet pattern.
indirectIndices Record<string, 1 | true> Legacy — {IndexName: 1} for keys-only GSIs that need a second-hop BatchGet. Synthesised into indices at construction.

Filter grammar (REST ?<op>-<field>=<value> allowlist)

Field Type Notes
filterable Record<string, Ops[] | {ops, type?}> Allowlist for the filter grammar (?<op>-<field>=<value>). Array shape ['eq', 'ge'] infers coercion type from keyFields / indices (default 'string'). Object shape {ops, type?: 'string' | 'number' | 'binary'} supplies an explicit type for fields not reachable from the key declarations (e.g. a year column). Requests naming an unlisted field or using an op not in the allowlist are rejected with BadFilterField / BadFilterOp.

Text search (?search=)

Field Type Notes
searchable Record<string, 1 | true> Fields that get a searchablePrefix + field lowercase mirror; the built-in prepare step writes the mirrors on every full write or patch.
searchablePrefix string Default '-search-'. Must start with technicalPrefix when that's set.

Concurrency and scope-freeze (opt-in, require technicalPrefix)

Field Type Notes
versionField string Numeric optimistic-concurrency attribute. post initialises to 1 with attribute_not_exists(<pk>); put conditions on attribute_not_exists(<pk>) OR <versionField> = :observed, {force: true} bypasses; patch(key, partial, {expectedVersion}) conditions when supplied; mass-op editListByParams CCF routes to conflicts: [{key, reason: 'VersionConflict'}] when declared.
createdAtField string Scope-freeze field for mass ops. {asOf: Date | string | number} AND-merges <createdAtField> <= :asOf into the FilterExpression; without declaration, asOf throws CreatedAtFieldNotDeclared. No auto-write — user's prepare hook owns the format (see the canned stampCreatedAtISO / stampCreatedAtEpoch builders).

Cascade gate (opt-in)

Field Type Notes
relationships {structural: true} Declares that this adapter owns a structural subtree. Required by deleteAllUnder / cloneAllUnder{,By} / moveAllUnder{,By}; throws CascadeNotDeclared otherwise.

Descriptor record (opt-in)

Field Type Notes
descriptorKey string Reserved-key value for the JSON descriptor record (typical: '__adapter__'). ensureTable writes a declaration snapshot under __toolkit_descriptor__ on the record at {keyFields[0]: descriptorKey}; verifyTable reads it back to detect drift DescribeTable can't see (marshalling fields, searchable mirrors, filterable allowlist). List-op Scans auto-hide this record via an injected FilterExpression; pass {includeDescriptor: true} to see it.

Projection + hooks

Field Type Notes
projectionFieldMap Record<string, string> Alias → real field for projections. Used by Expressions: Projection builder.
retry RetryOptions Default retry policy ({backoff?, maxAttempts?}) for chunked BatchWriteItem / BatchGetItem in bulk methods; per-call options.retry overrides. Default: exponential + jitter, 50 ms → 20 s cap, 8 attempts. Transactions excluded. See Batch and transactions.
hooks AdapterHooks<TItem> Per-instance hook overrides; merges over defaults. See Adapter: Hooks.

The constructor throws synchronously when client, table, or a non-empty keyFields is missing, and when technicalPrefix-dependent options are declared without it.

Example — typical hierarchical adapter

import {Adapter, stampCreatedAtISO} from 'dynamodb-toolkit';
import {marshallDateISO, unmarshallDateISO} from 'dynamodb-toolkit/marshalling';

const adapter = new Adapter({
  client: docClient,
  table: 'rentals',

  technicalPrefix: '_',
  keyFields: ['state', 'facility', 'vehicle'],
  structuralKey: '_sk',

  typeLabels: ['state', 'facility', 'vehicle'],
  typeField: 'kind',
  typeDiscriminator: 'kind',

  indices: {
    'by-status-createdAt': {type: 'gsi', pk: 'status', sk: '_createdAt', projection: 'all'},
    'by-price': {type: 'lsi', sk: {name: 'dailyPriceCents', type: 'number'}, projection: 'keys-only'}
  },

  filterable: {
    kind: ['eq', 'in'],
    status: ['eq', 'ne', 'in'],
    dailyPriceCents: ['lt', 'le', 'gt', 'ge', 'btw'],
    year: {ops: ['eq', 'ge', 'le', 'btw'], type: 'number'}
  },

  versionField: '_version',
  createdAtField: '_createdAt',
  relationships: {structural: true},
  descriptorKey: '__adapter__',

  hooks: {
    prepare: stampCreatedAtISO('_createdAt')
  }
});

See the runnable examples/car-rental/ walkthrough for a larger end-to-end example with marshalling, multi-tier records, and every list-op.

Dropped from v2

specialTypes, converter, converterOptions, the DocumentClient sniff (isDocClient) — all removed. v3 always speaks DynamoDBDocumentClient; Sets vs Lists are handled natively by lib-dynamodb's marshalling. See Migration: v2 to v3.

Clone this wiki locally