The Document Client simplifies working with Amazon DynamoDB by abstracting away AttributeValue shapes. Instead of wrapping every value in a type descriptor like { S: "hello" }, you pass plain JavaScript objects and the client handles marshalling and unmarshalling automatically.
Installation
npm install @aws-sdk/lib-dynamodb @aws-sdk/client-dynamodb
Type mapping
The Document Client maps between native JavaScript types and DynamoDB AttributeValue shapes:
| JavaScript type | DynamoDB AttributeValue |
|---|
| String | S |
| Number / BigInt / NumberValue | N |
| Boolean | BOOL |
| null | NULL |
| Array | L |
| Object | M |
| Set<Uint8Array, Blob, …> | BS |
| Set<Number, BigInt, NumberValue> | NS |
| Set<String> | SS |
| Uint8Array, Buffer, File, Blob | B |
For example, the following raw DynamoDB list value:
{ "L": [{ "NULL": true }, { "BOOL": false }, { "N": 1 }, { "S": "two" }] }
Is represented by the Document Client as a plain JavaScript array:
Creating the Document Client
First create a DynamoDBClient, then wrap it with DynamoDBDocumentClient.from():
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);
Use DynamoDBDocumentClient (bare-bones) for smaller bundle sizes. Use DynamoDBDocument (full client) if you prefer calling methods like docClient.put() directly instead of sending Command objects.
Alternatively, use the full document client:
import { DynamoDB } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";
const client = new DynamoDB({});
const docClient = DynamoDBDocument.from(client);
// Call operations directly as methods
await docClient.put({
TableName: "MyTable",
Item: {
id: "2",
content: "content from DynamoDBDocument",
},
});
Available commands
The following commands are available for use with DynamoDBDocumentClient:
GetCommand
PutCommand
DeleteCommand
UpdateCommand
QueryCommand
ScanCommand
BatchGetCommand
BatchWriteCommand
TransactGetCommand
TransactWriteCommand
Examples
Writing an item with PutCommand
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";
const docClient = DynamoDBDocumentClient.from(client);
await docClient.send(
new PutCommand({
TableName: "MyTable",
Item: {
id: "1",
content: "content from DynamoDBDocumentClient",
},
})
);
Use the built-in paginator for full table scans:
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, paginateScan, ScanCommand } from "@aws-sdk/lib-dynamodb";
const client = new DynamoDBClient({ region: "us-west-2" });
const docClient = DynamoDBDocumentClient.from(client);
const paginatorConfiguration = { client: docClient };
// The input is wrapped in a function to avoid mutation across pages
const scanRequestInput = () => ({
TableName: "YOUR_TABLE_NAME",
Limit: 100,
});
let pageNumber = 1;
for await (const page of paginateScan(paginatorConfiguration, scanRequestInput())) {
console.log("page:", pageNumber++);
console.log(page.Items);
}
You can also handle pagination manually using the LastEvaluatedKey token:
const firstPage = await docClient.send(new ScanCommand(scanRequestInput()));
let paginationToken = firstPage.LastEvaluatedKey;
while (paginationToken) {
const page = await docClient.send(
new ScanCommand({
...scanRequestInput(),
ExclusiveStartKey: paginationToken,
})
);
paginationToken = page.LastEvaluatedKey;
console.log(page.Items);
}
Before performing a full table scan, consider whether BatchGetItem or a Query would be more efficient.
Marshalling options
Pass a translateConfig object as the second argument to DynamoDBDocumentClient.from() to control how values are marshalled and unmarshalled.
const marshallOptions = {
// Convert empty strings, blobs, and sets to null
convertEmptyValues: false,
// Remove undefined values from objects and arrays
removeUndefinedValues: false,
// Convert plain objects (class instances) to DynamoDB map attributes
convertClassInstanceToMap: false,
// Allow numbers beyond Number.MAX_SAFE_INTEGER (may lose precision)
allowImpreciseNumbers: false,
};
const unmarshallOptions = {
// Return numbers as NumberValue instead of native JS numbers
wrapNumbers: false,
};
const translateConfig = { marshallOptions, unmarshallOptions };
const client = new DynamoDBClient({});
const docClient = DynamoDBDocument.from(client, translateConfig);
marshallOptions
| Option | Type | Default | Description |
|---|
convertEmptyValues | boolean | false | Convert empty strings, blobs, and sets to null |
removeUndefinedValues | boolean | false | Strip undefined values from objects and arrays |
convertClassInstanceToMap | boolean | false | Marshal class instances as DynamoDB map attributes |
allowImpreciseNumbers | boolean | false | Allow numbers beyond Number.MAX_SAFE_INTEGER, which may lose precision |
unmarshallOptions
| Option | Type | Default | Description |
|---|
wrapNumbers | boolean | ((str: string) => any) | false | When true, return numbers as NumberValue. Pass a function for custom conversion (e.g. BigInt) |
Large numbers and NumberValue
By default, the Document Client throws an error if a number exceeds Number.MAX_SAFE_INTEGER to prevent silent precision loss. Use NumberValue to store and retrieve large numbers precisely.
Writing large numbers
import { DynamoDB } from "@aws-sdk/client-dynamodb";
import { NumberValue, DynamoDBDocument } from "@aws-sdk/lib-dynamodb";
const docClient = DynamoDBDocument.from(new DynamoDB({}));
await docClient.put({
Item: {
id: 1,
smallNumber: NumberValue.from("123"),
bigNumber: NumberValue.from("1000000000000000000000.000000000001"),
nSet: new Set([123, NumberValue.from("456"), 789]),
},
});
Reading large numbers
Configure wrapNumbers to control how numbers are returned on reads:
const docClient = DynamoDBDocument.from(new DynamoDB({}), {
unmarshallOptions: {
// Use BigInt for all numbers
wrapNumbers: (str) => BigInt(str),
},
});
const response = await docClient.get({ Key: { id: 1 } });
// Numbers in response are BigInt values
NumberValue does not support mathematical operations. Call .toString() on a NumberValue to pass it to a big number library.
Cleaning up
destroy() on the Document Client is a no-op — it does not own the underlying DynamoDBClient. Call destroy() on the original client to release resources:
const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);
// ...perform operations...
docClient.destroy(); // no-op
client.destroy(); // releases DynamoDBClient resources