DynamoDB BatchWriteItem in Node.js (AWS SDK v3)

BatchWriteItem puts or deletes up to 25 items in one request. It is not a smaller UpdateItem: every PutRequest replaces the whole stored item, and the v3 types give you nowhere to attach a condition. Batch operations in DynamoDB covers the limits and the partial-failure model; this page is about the v3 call and the one way it loses data quietly.

Code

import {BatchWriteItemCommand, DynamoDBClient} from '@aws-sdk/client-dynamodb';

const client = new DynamoDBClient({region: 'us-east-1'});

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

let requestItems = {
  Music: [
    {
      PutRequest: {
        Item: {
          Artist: {S: 'Arturo Sandoval'},
          SongTitle: {S: 'Cubano Chant'},
          AlbumTitle: {S: 'Danzon'},
          Year: {N: '1994'}
        }
      }
    },
    {
      PutRequest: {
        Item: {
          Artist: {S: 'Arturo Sandoval'},
          SongTitle: {S: 'A Mis Abuelos'},
          AlbumTitle: {S: 'Danzon'},
          Year: {N: '1994'}
        }
      }
    },
    {
      DeleteRequest: {
        Key: {Artist: {S: 'Ella Fitzgerald'}, SongTitle: {S: 'Misty'}}
      }
    }
  ]
};

let attempt = 0;

do {
  const response = await client.send(new BatchWriteItemCommand({RequestItems: requestItems}));

  // Writes that were throttled come back in UnprocessedItems — resubmit them
  // with exponential backoff until the map is empty.
  requestItems = response.UnprocessedItems;
  if (requestItems && Object.keys(requestItems).length > 0) {
    attempt += 1;
    await sleep(Math.min(100 * 2 ** attempt, 5000));
  }
} while (requestItems && Object.keys(requestItems).length > 0);

console.log('Batch written');

Explanation

  • UnprocessedItems, not UnprocessedKeys — the leftover member. The read side uses the other name, and in JavaScript a typo here compiles, reads as undefined, and turns the do/while into a single-pass call that drops throttled writes on the floor. TypeScript catches it; plain JS does not.
  • There is nowhere to put a condition. The v3 WriteRequest type has exactly two optional members, PutRequest and DeleteRequest, and neither accepts ConditionExpression or ReturnValues. This is not the SDK being conservative: the API reference says you cannot specify conditions on individual put and delete requests. If a write needs a guard it does not belong in a batch, it belongs in UpdateItem with a condition or a transaction.
  • Two catch-able mistakes, both non-retryable, discriminated on err.name. Twenty-six entries raises ValidationException / Too many items requested for the BatchWriteItem call. Touching one key twice raises Provided list of item keys contains duplicates, and that message covers a put+delete pair as well as two puts, which reads oddly the first time you see it.
  • Whole-batch rejection list — longer than the obvious three. Alongside >25 requests, a >400 KB item and >16 MB total, DynamoDB refuses the batch for a missing table, a key that does not match the schema, a partition key over 2048 bytes, or a sort key over 1024 bytes. One bad entry costs you all 25.
  • Batching buys round trips, not capacity. Each entry is billed as an individual PutItem or DeleteItem, rounded up to 1 KB, and a delete aimed at a nonexistent item still consumes a write unit.

A key-only PutRequest destroys the rest of the item

Ella Fitzgerald / Misty starts out with an AlbumTitle and a Year. Send one PutRequest carrying only the two key attributes:

{PutRequest: {Item: {Artist: {S: 'Ella Fitzgerald'}, SongTitle: {S: 'Misty'}}}}

Then read it back with ConsistentRead: true. DynamoDB Local 3.3.0 returns:

{
  "Artist": { "S": "Ella Fitzgerald" },
  "SongTitle": { "S": "Misty" }
}

AlbumTitle and Year are gone. The call succeeded, UnprocessedItems was {}, and nothing in the response mentions the two attributes it dropped. A put is a whole-item replace, so a batch assembled from a partial payload (an API request body, a CSV column subset, a projected Query result that omitted attributes) erases every attribute the payload did not carry.

That is the failure mode to plan around when you use a batch for what feels like an update. The fix is to read the current item first and merge, or to stop batching and use UpdateItem, which touches only the attributes you name.

The other reason a 25-item batch becomes a 12-item batch is size. Writes round up to 1 KB each for billing and the request caps at 16 MB, so an item's real byte count decides both your bill and how many fit. The item size calculator gives you that number per item before you assemble the array.

To load, edit and delete items in bulk without hand-writing the replace semantics, download DynoTable.

References

Last verified 2026-07-28 against the official AWS documentation linked above.

Work with DynamoDB without the Console

A fast DynamoDB desktop client that runs the real SQL DynamoDB can’t — JOINs, GROUP BY, aggregates — with visual editing and an AI agent on your own Bedrock keys.

Free 30-day trial, no credit card — then the Free plan with no time limit.