Skip to main content

Packages

PackagePurpose
@aws-sdk/client-dynamodbLow-level client. All values must use DynamoDB AttributeValue types (e.g., { S: "hello" }).
@aws-sdk/lib-dynamodbDocument Client wrapper. Accepts and returns plain JavaScript types. Recommended for application code.
Use the Document Client (@aws-sdk/lib-dynamodb) for application code. It marshals native JavaScript types to AttributeValue automatically, so you write { name: "Alice" } instead of { name: { S: "Alice" } }.

Installation

npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb

Creating the clients

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

// Base DynamoDB client
const ddbClient = new DynamoDBClient({ region: "us-east-1" });

// Document Client wrapping the base client
const docClient = DynamoDBDocumentClient.from(ddbClient);
You can pass optional marshalling configuration to DynamoDBDocumentClient.from:
const docClient = DynamoDBDocumentClient.from(ddbClient, {
  marshallOptions: {
    removeUndefinedValues: true,
    convertClassInstanceToMap: true,
  },
  unmarshallOptions: {
    wrapNumbers: false,
  },
});

Commands

The examples below use the Document Client commands from @aws-sdk/lib-dynamodb. Each section also shows the equivalent low-level command from @aws-sdk/client-dynamodb.
// Document Client (recommended)
import { DynamoDBDocumentClient, GetCommand } from "@aws-sdk/lib-dynamodb";

const response = await docClient.send(
  new GetCommand({
    TableName: "Users",
    Key: {
      userId: "user-123",
    },
  })
);

console.log(response.Item); // Plain JS object, e.g. { userId: "user-123", name: "Alice" }
// Low-level client
import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";

const response = await ddbClient.send(
  new GetItemCommand({
    TableName: "Users",
    Key: {
      userId: { S: "user-123" },
    },
  })
);

console.log(response.Item); // { userId: { S: "user-123" }, name: { S: "Alice" } }
// Document Client (recommended)
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";

await docClient.send(
  new PutCommand({
    TableName: "Users",
    Item: {
      userId: "user-456",
      name: "Bob",
      age: 30,
      active: true,
      tags: ["admin", "developer"],
    },
    // Optional: fail if the item already exists
    ConditionExpression: "attribute_not_exists(userId)",
  })
);
import { DynamoDBDocumentClient, UpdateCommand } from "@aws-sdk/lib-dynamodb";

await docClient.send(
  new UpdateCommand({
    TableName: "Users",
    Key: {
      userId: "user-123",
    },
    UpdateExpression: "SET #name = :name, age = :age",
    ExpressionAttributeNames: {
      "#name": "name", // "name" is a reserved word in DynamoDB
    },
    ExpressionAttributeValues: {
      ":name": "Alice Smith",
      ":age": 31,
    },
    ReturnValues: "ALL_NEW",
  })
);
Use ExpressionAttributeNames to alias reserved words or attribute names with special characters.
import { DynamoDBDocumentClient, DeleteCommand } from "@aws-sdk/lib-dynamodb";

await docClient.send(
  new DeleteCommand({
    TableName: "Users",
    Key: {
      userId: "user-123",
    },
    // Optional: only delete if a condition is met
    ConditionExpression: "attribute_exists(userId)",
  })
);
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const response = await docClient.send(
  new QueryCommand({
    TableName: "Orders",
    KeyConditionExpression: "customerId = :customerId AND createdAt > :since",
    ExpressionAttributeValues: {
      ":customerId": "customer-abc",
      ":since": "2024-01-01",
    },
    // Optional: filter results further (applied after key condition)
    FilterExpression: "status = :status",
    ExpressionAttributeValues: {
      ":customerId": "customer-abc",
      ":since": "2024-01-01",
      ":status": "shipped",
    },
    ScanIndexForward: false, // Descending sort by sort key
    Limit: 20,
  })
);

console.log(response.Items); // Array of plain JS objects
console.log(response.Count); // Number of items returned
QueryCommand requires a partition key condition. To query a GSI, add IndexName.
import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";

const response = await docClient.send(
  new ScanCommand({
    TableName: "Users",
    FilterExpression: "age > :minAge",
    ExpressionAttributeValues: {
      ":minAge": 18,
    },
    Limit: 100,
  })
);

console.log(response.Items);
A ScanCommand reads every item in the table. Use QueryCommand whenever possible to avoid excessive read capacity consumption.
import { DynamoDBDocumentClient, BatchGetCommand } from "@aws-sdk/lib-dynamodb";

const response = await docClient.send(
  new BatchGetCommand({
    RequestItems: {
      Users: {
        Keys: [
          { userId: "user-123" },
          { userId: "user-456" },
          { userId: "user-789" },
        ],
        ProjectionExpression: "userId, #name",
        ExpressionAttributeNames: { "#name": "name" },
      },
    },
  })
);

console.log(response.Responses?.Users);
// Items that could not be retrieved due to throttling
console.log(response.UnprocessedKeys);
A single BatchGetCommand can retrieve up to 100 items across one or more tables. Retry UnprocessedKeys with exponential backoff if present.
import { DynamoDBDocumentClient, BatchWriteCommand } from "@aws-sdk/lib-dynamodb";

await docClient.send(
  new BatchWriteCommand({
    RequestItems: {
      Users: [
        {
          PutRequest: {
            Item: { userId: "user-new-1", name: "Carol", age: 25 },
          },
        },
        {
          PutRequest: {
            Item: { userId: "user-new-2", name: "Dave", age: 40 },
          },
        },
        {
          DeleteRequest: {
            Key: { userId: "user-old" },
          },
        },
      ],
    },
  })
);
A single BatchWriteCommand can contain up to 25 put or delete requests. Retry UnprocessedItems with exponential backoff if present.

Pagination

Use paginateQuery or paginateScan from @aws-sdk/lib-dynamodb to automatically iterate all pages:
import {
  DynamoDBDocumentClient,
  paginateQuery,
  paginateScan,
} from "@aws-sdk/lib-dynamodb";

// Paginated query
for await (const page of paginateQuery(
  { client: docClient },
  {
    TableName: "Orders",
    KeyConditionExpression: "customerId = :customerId",
    ExpressionAttributeValues: { ":customerId": "customer-abc" },
  }
)) {
  console.log(page.Items);
}

// Paginated scan
for await (const page of paginateScan(
  { client: docClient },
  {
    TableName: "Users",
    Limit: 100,
  }
)) {
  console.log(page.Items);
}

Condition expressions

Use condition expressions with PutCommand, UpdateCommand, and DeleteCommand to implement optimistic locking and prevent unintended overwrites:
import { DynamoDBDocumentClient, UpdateCommand } from "@aws-sdk/lib-dynamodb";
import { ConditionalCheckFailedException } from "@aws-sdk/client-dynamodb";

try {
  await docClient.send(
    new UpdateCommand({
      TableName: "Users",
      Key: { userId: "user-123" },
      // Only update if the current version matches
      ConditionExpression: "version = :currentVersion",
      UpdateExpression: "SET #data = :newData, version = :newVersion",
      ExpressionAttributeNames: { "#data": "data" },
      ExpressionAttributeValues: {
        ":currentVersion": 5,
        ":newData": "updated value",
        ":newVersion": 6,
      },
    })
  );
} catch (err) {
  if (err instanceof ConditionalCheckFailedException) {
    console.log("Version mismatch — item was modified by another process");
  }
  throw err;
}

Document Client

Learn more about marshalling, unmarshalling, and large number handling in @aws-sdk/lib-dynamodb.

Build docs developers (and LLMs) love