DynamoDB DocumentClient Usage with Native JavaScript Types: Complete Guide

The DynamoDB DocumentClient (@aws-sdk/lib-dynamodb) automatically marshals native JavaScript primitives, arrays, and objects into DynamoDB's AttributeValue format, eliminating manual type conversion while providing TypeScript-safe inference and configurable number handling.

Working with Amazon DynamoDB in modern JavaScript applications typically requires translating between native language types and the service's low-level wire format. According to the aws/agent-toolkit-for-aws repository, the DynamoDB DocumentClient provides a high-level abstraction that handles this conversion transparently, allowing developers to work with plain JavaScript objects while the SDK manages the underlying { S: "...", N: "123" } structures internally.

Architecture and Type Conversion

The DocumentClient operates as a thin wrapper around the base DynamoDBClient from @aws-sdk/client-dynamodb. When you send commands through the DocumentClient, the library intercepts inputs and outputs to perform automatic serialization.

Marshalling occurs before requests leave your application. The client inspects JavaScript values and converts them according to a static mapping table: strings become S types, numbers become N types, booleans become BOOL, arrays become L (lists), and objects become M (maps). This transformation happens in skills/core-skills/aws-sdk-js-v3-usage/references/dynamodb.md lines 32-43, which defines the type correspondence logic used by the SDK.

Unmarshalling reverses the process on responses. By default, DynamoDB numbers convert to JavaScript number primitives. However, for arbitrary-precision arithmetic, you can enable the wrapNumbers option to receive NumberValue objects instead, as documented in the reference file lines 55-57.

Configuration Options for Marshalling Behavior

You can customize how the DocumentClient handles edge cases through marshallOptions and unmarshallOptions passed to the factory method.

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

const client = DynamoDBDocumentClient.from(new DynamoDBClient({}), {
  marshallOptions: {
    removeUndefinedValues: true,  // Strip undefined fields from items
    convertEmptyValues: false,    // Preserve empty strings rather than converting to null
  },
  unmarshallOptions: {
    wrapNumbers: true,            // Return NumberValue for all numeric data
  },
});

These configuration parameters are detailed in skills/core-skills/aws-sdk-js-v3-usage/references/dynamodb.md lines 47-55. The removeUndefinedValues option is particularly useful when working with sparse schemas, preventing the SDK from sending empty AttributeValue objects to the service.

Handling Large Numbers and Binary Data

JavaScript's number type cannot safely represent integers larger than 2^53 - 1. When working with DynamoDB attributes that exceed Number.MAX_SAFE_INTEGER, use the NumberValue class to maintain precision.

import { NumberValue, DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";

const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));

await client.send(
  new PutCommand({
    TableName: "FinancialRecords",
    Item: {
      accountId: "ACC-123",
      balance: NumberValue.from("1000000000000000000000.000000001"),
    },
  })
);

This pattern, documented in lines 60-70 of the DynamoDB reference file, ensures that monetary values and other high-precision data remain intact without floating-point corruption. The DocumentClient also natively supports JavaScript Set objects and binary data (Buffer or Uint8Array), converting them to DynamoDB's SS, NS, BS, and B types automatically.

CRUD Operations with Native Types

The following example demonstrates basic create, read, update, and delete operations using plain JavaScript objects. Notice the absence of AttributeValue notation—the DocumentClient handles the marshalling internally.

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import {
  DynamoDBDocumentClient,
  PutCommand,
  GetCommand,
  QueryCommand,
  DeleteCommand,
} from "@aws-sdk/lib-dynamodb";

const client = DynamoDBDocumentClient.from(
  new DynamoDBClient({ region: "us-east-1" })
);

// Create - native types marshaled automatically
await client.send(
  new PutCommand({
    TableName: "Users",
    Item: { 
      id: "user-456", 
      name: "Alice", 
      age: 30, 
      active: true,
      tags: new Set(["admin", "beta"])
    },
  })
);

// Read
const { Item } = await client.send(
  new GetCommand({ TableName: "Users", Key: { id: "user-456" } })
);
console.log(Item); // { id: 'user-456', name: 'Alice', age: 30, active: true, tags: Set { ... } }

// Query with expression attributes
const { Items } = await client.send(
  new QueryCommand({
    TableName: "Users",
    KeyConditionExpression: "id = :id",
    ExpressionAttributeValues: { ":id": "user-456" },
  })
);

// Delete
await client.send(
  new DeleteCommand({ TableName: "Users", Key: { id: "user-456" } })
);

This implementation mirrors the examples found in skills/core-skills/aws-sdk-js-v3-usage/references/dynamodb.md lines 7-28, which serve as the authoritative reference for the Agent Toolkit.

Pagination with Native Type Unmarshalling

The DocumentClient integrates seamlessly with the SDK's paginator utilities, automatically unmarshalling each page of results into native JavaScript arrays.

import { paginateScan } from "@aws-sdk/lib-dynamodb";

const paginator = paginateScan(
  { client }, 
  { TableName: "Users", Limit: 100 }
);

for await (const page of paginator) {
  console.log(page.Items); // Already converted from AttributeValue format
}

As shown in lines 82-89 of the reference documentation, the paginateScan function returns an async iterator where each page contains native JavaScript objects rather than raw DynamoDB attribute maps.

Summary

  • DynamoDB DocumentClient wraps the low-level DynamoDBClient to provide automatic marshalling between native JavaScript types and DynamoDB AttributeValue formats.
  • Configuration options (marshallOptions and unmarshallOptions) let you control undefined value handling, empty string conversion, and number wrapping behavior.
  • Large number support via the NumberValue class prevents precision loss when working with values exceeding JavaScript's safe integer limits.
  • Pagination utilities like paginateScan work transparently with the DocumentClient, returning unmarshalled JavaScript objects in each iteration.
  • Source files in aws/agent-toolkit-for-aws (specifically skills/core-skills/aws-sdk-js-v3-usage/references/dynamodb.md and SKILL.md) provide the authoritative implementation details for these patterns.

Frequently Asked Questions

How does DynamoDB DocumentClient handle JavaScript undefined values?

By default, the DocumentClient throws an error when encountering undefined values in item attributes. However, you can configure marshallOptions.removeUndefinedValues: true during client creation to silently strip these fields before sending them to DynamoDB. This behavior is defined in the marshalling options section of the Agent Toolkit reference documentation.

Can I use DynamoDB DocumentClient with TypeScript generics for type safety?

Yes. The DocumentClient ships with TypeScript definitions that allow you to specify the shape of your items using generics on command constructors. For example, new GetCommand<{ id: string; count: number }>(...) provides compile-time checking for attribute names and types, while the SDK still handles the runtime marshalling to AttributeValue format.

What is the performance difference between DocumentClient and the low-level DynamoDB client?

The DocumentClient adds a small serialization overhead for converting between native types and AttributeValue structures. For performance-critical workloads requiring microsecond-level optimization, you can bypass this layer by using the raw DynamoDBClient from @aws-sdk/client-dynamodb and constructing AttributeValue objects manually. However, for most applications, the convenience and reduced error rate of automatic marshalling outweigh the minimal overhead.

How do I store binary data using DynamoDB DocumentClient?

Pass Buffer, Uint8Array, or Blob objects directly in your item attributes. The DocumentClient automatically converts these to DynamoDB's B (binary) type during marshalling. When reading back, the values are returned as Uint8Array instances by default, preserving the original binary data without manual Base64 encoding or decoding.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →