DatriseAI-first ETL

Upsert generator

Upsert into Amazon DynamoDB: PutItem with ConditionExpression

Name your table, key and columns and get the statement Amazon DynamoDB actually accepts, with a guard so an older row never overwrites a newer one. Below it: how the mechanic works, what breaks, and how Datrise loads Amazon DynamoDB incrementally.

Generate the statement

The URL updates as you type; share it to hand someone the exact form.

Statement · PutItem with ConditionExpression

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

// item: { id, name, stage, amount, owner_id, updated_at }
async function upsert(ddb, item) {
  try {
    await ddb.send(new PutCommand({
      TableName: "deals",
      Item: item,
      ConditionExpression: "attribute_not_exists(#pk) OR #wm < :wm",
      ExpressionAttributeNames: { "#pk": "id", "#wm": "updated_at" },
      ExpressionAttributeValues: { ":wm": item.updated_at },
    }));
  } catch (err) {
    if (err.name !== "ConditionalCheckFailedException") throw err;
    // stale version: a newer item is already stored, skip it
  }
}

How the upsert works in Amazon DynamoDB

DynamoDB has PutItem, which writes the whole item, and UpdateItem, which edits attributes; neither is an upsert until you add a ConditionExpression. The generated code writes the item only when the key does not exist yet or the stored updated_at is older, and treats ConditionalCheckFailedException as "stale, skip". That gives you a watermark upsert with one request per item.

Batching changes the trade-off. BatchWriteItem takes 25 items but cannot carry a condition, so it is last-write-wins; TransactWriteItems takes 100 conditional writes at twice the cost. Design the partition key from the entity id so writes spread evenly, and keep items small: write capacity is billed per 1 KB, and a nested CRM record with long text lists can cost several units per upsert.

Before you run it

  • PutItem replaces the whole item. Use UpdateItem with SET expressions when the source sends partial records, or attributes the source stopped sending will vanish.
  • BatchWriteItem (25 items) cannot carry a ConditionExpression; TransactWriteItems (100 items) can, at twice the write cost. For plain last-write-wins, batch; for the watermark guard, transact or loop.
  • Write capacity is billed per 1 KB of item size, rounded up. A wide CRM record with nested lists costs several WCUs per upsert; trim it before the write, not after.

Questions people ask

PutItem or UpdateItem for a sync?

PutItem when the staged item is complete. UpdateItem with SET when the source sends partial records, otherwise attributes it omitted are erased.

Can BatchWriteItem apply the updated_at guard?

No. Batch writes are unconditional. Use TransactWriteItems for conditional batches, or accept last-write-wins and dedupe the batch by key before sending.

How do I handle a hot partition during backfill?

Backfills hit many keys at once, which is fine when the partition key is the entity id. Problems come from a low-cardinality key such as account_id; add the entity id to the key or use a sort key.

The same generator for other destinations

Skip writing the merge at all

Datrise lands CRM and SaaS entities into Amazon DynamoDB with this exact mechanic, a watermark on updated-at, and typed columns, so the statement above is what runs on your behalf. Join the waitlist to get early access.

Browse the integration catalog