Skip to content

Adapter: Raw marker

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

Adapter: Raw bypass marker

See also (AWS JS SDK v3): DocumentClient marshalling. Vocabulary: Concepts.

Raw<T> is the v3 bypass marker for items already shaped for DynamoDB. It replaces the v2 Raw / DbRaw pair — there's just one marker now, since v3 always speaks DynamoDBDocumentClient.

import {Raw, raw} from 'dynamodb-toolkit';

const wrapped = raw({name: 'X', '-internal': 'kept'});
wrapped instanceof Raw;  // true
wrapped.item;            // {name: 'X', '-internal': 'kept'}

Effects on writes

When the Adapter sees a Raw<T> on post / put / patch:

  • prepare is skipped — your item flows verbatim into Item / UpdateExpression.
  • validateItem is skipped — caller asserts the item is already valid.
  • Marshalling still runslib-dynamodb's middleware encodes Set / Buffer / Number etc. unconditionally.
await adapter.put(raw({name: 'X', '-search-name': 'x', '-t': 1}), {force: true});
// Written as-is; prepare/validateItem not called

About the --prefixed fields above. '-search-name' is a searchable mirror column that would normally be populated by the prepare hook from Expressions: Filter builder; '-t' is an author-convention technical field (the toolkit does not reserve it). The convention is only enforced by your own prepare / revive hooks — so bypassing prepare with raw() means you're bypassing the only thing that keeps the write self-consistent. You have to:

  1. Add every technical field your revive expects to strip ('-t', '-search-name', '-search-climate', …), otherwise the round-trip read will come back with garbage fields visible or missing the markers your list queries depend on.
  2. Match the exact naming convention your hooks use — rename -t_t in one place and both prepare and revive have to change together, but raw() writes skip both and keep writing whatever you put in the object.

If you find yourself repeatedly mirroring prepare inside raw() call sites, the common fix is a small helper that calls adapter.hooks.prepare(item) explicitly and then wraps the result in raw(). See Concepts → technicalPrefix and managed fields for the broader pattern.

Effects on reads

Pass {reviveItems: false} on a read method to receive Raw<TItem> instances:

const item = await adapter.getByKey({name: 'X'}, undefined, {reviveItems: false});
// item instanceof Raw === true; item.item is the raw record (technical fields visible)

const items = await adapter.getByKeys([{name: 'X'}, {name: 'Y'}], undefined, {reviveItems: false});
// items.forEach(i => i instanceof Raw)

This is useful when piping data between adapters — the source adapter strips technical fields with its revive, then the destination adapter would have to re-add them via prepare. With Raw, you skip both round-trips.

Migrating from v2

v2 v3
new Adapter.Raw(item) raw(item)
new Adapter.DbRaw(item) raw(item) (collapsed)
Adapter.markAsRaw(item) raw(item)
returnRaw: 'raw' / 'db-raw' {reviveItems: false}

Notes

  • Raw is a regular ES class; the brand is the class identity. x instanceof Raw is the check.
  • The item field holds the verbatim wrapped value. Mutate it and the next write picks up the change.
  • Raw works for keys too — getByKey(raw({name: 'X'})) skips prepareKey.

Clone this wiki locally