DynamoDB Conditional Write in Node.js (AWS SDK v3)

Der interessante Teil eines bedingten Writes im AWS SDK v3 ist nicht die ConditionExpression — die funktioniert überall gleich und ist in DynamoDB Condition Expressions behandelt. Es ist der Fehlerpfad: v3 reicht dir das unterlegene Item am geworfenen Fehler mit, wenn du danach gefragt hast, und gibt dir nichts, wenn nicht.

Code

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

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

// Update the item only if nobody changed it since we read version 7.
const command = new UpdateItemCommand({
  TableName: 'Music',
  Key: {
    Artist: {S: 'Arturo Sandoval'},
    SongTitle: {S: 'Cubano Chant'}
  },
  UpdateExpression: 'SET #upd0 = :updValue0, #version = :newVersion',
  ConditionExpression: 'attribute_exists(#cond0) AND #version = :expectedVersion',
  ExpressionAttributeNames: {
    '#upd0': 'Genre',
    '#version': 'Version',
    '#cond0': 'Artist'
  },
  ExpressionAttributeValues: {
    ':updValue0': {S: 'Latin Jazz'},
    ':expectedVersion': {N: '7'},
    ':newVersion': {N: '8'}
  },
  ReturnValuesOnConditionCheckFailure: 'ALL_OLD'
});

try {
  await client.send(command);
  console.log('Updated to version 8');
} catch (err) {
  if (err.name === 'ConditionalCheckFailedException') {
    // With ReturnValuesOnConditionCheckFailure: 'ALL_OLD', the current item
    // rides back on the exception — no extra read to see what beat you.
    console.log('Lost the race — item is now:', err.Item);
  } else {
    throw err;
  }
}

Erklärung

  • Die fehlgeschlagene Prüfung ist ein geworfener Fehler, kein Statusfeld. v3 rejectet das Promise, der Write-Pfad und der Pfad des verlorenen Rennens sind also verschiedene Zweige. err.name === 'ConditionalCheckFailedException' ist die Unterscheidung; alles andere muss erneut geworfen werden — dafür ist das else im Fence da. Schluckst du das ganze catch, hast du eine Drosselung stillschweigend in einen No-Op verwandelt.
  • ReturnValuesOnConditionCheckFailure ist der einzige Weg zu sehen, wer dir zuvorgekommen ist. Ohne das trägt der Fehler nur die Meldung und sonst nichts, und du bist zurück bei einem GetItem, das du nicht gebraucht hättest. Die API-Referenz legt die gültigen Werte auf ALL_OLD | NONE fest und bestätigt, dass es keine Lesekapazität verbraucht.
  • err.Item ist eine rohe AttributeValue-Map, in derselben Form wie der Key, den du geschickt hast, und kein einfaches JavaScript. Schick es durch unmarshall aus @aws-sdk/util-dynamodb, bevor du Version mit einer Zahl vergleichst, sonst vergleichst du gegen {N: '9'}.
  • Der fehlgeschlagene Write wird trotzdem abgerechnet. Der Developer Guide sagt ausdrücklich, dass eine zu false ausgewertete Bedingung weiterhin Schreibkapazität verbraucht, bemessen am größeren von altem und neuem Item. Eine Retry-Schleife auf einem heißen Key ist eine echte Position auf der Rechnung — deckle also die Versuche.
  • Jeder Name im Fence ist aliasiert (#versionVersion, #cond0Artist), weil der Expression Builder, der ihn generiert hat, bedingungslos aliasiert. Das ist hier schwerer als nötig und nie falsch — das ist der Kompromiss, den er eingeht.

Die Kopie des Verlierers aus der Exception lesen

Setze das gespeicherte Version auf 9 und führe den Fence aus, der 7 erwartet. DynamoDB Local 3.3.0 wirft, und der gefangene Fehler trägt:

err.name     ConditionalCheckFailedException
err.message  The conditional request failed
err.$metadata.httpStatusCode  400
err.Item     {
               Artist:     { S: 'Arturo Sandoval' },
               Year:       { N: '1994' },
               Version:    { N: '9' },
               SongTitle:  { S: 'Cubano Chant' },
               AlbumTitle: { S: 'Danzon' }
             }

Dieses Version: 9 ist der ganze Punkt. Der Retry kann direkt wieder durch das Update gehen, mit :expectedVersion auf 9 gesetzt — ohne zusätzlichen Read und ohne Fenster, in dem sich ein dritter Schreiber zwischen dein GetItem und deinen Retry schiebt.

Lösche ReturnValuesOnConditionCheckFailure aus demselben Command und führe es erneut aus. Gleicher name, gleiche message, gleiche 400 — und err.Item ist undefined. Nichts warnt dich: Der Parameter ist optional, sein Fehlen ist kein Fehler, und der Code, der err.Item liest, fängt einfach an, in der Produktion undefined zu loggen.

Beachte außerdem: Eine 400 bedeutet hier nicht, dass die Anfrage fehlerhaft war. ValidationException und ConditionalCheckFailedException teilen sich den Statuscode, und nur eine der beiden ist ein Bug — deshalb verzweigt man über err.name und nie über den Status.

Um eine Bedingung gegen deine eigenen Daten gelingen und scheitern zu sehen, mit einer Expression, die für dich geschrieben statt getippt wird, lade DynoTable herunter.

Verwandte Beispiele

Referenzen

Zuletzt verifiziert am 2026-07-28 gegen die oben verlinkte offizielle AWS-Dokumentation.

Mit DynamoDB ohne die Console arbeiten

Ein schneller DynamoDB-Desktop-Client, der das echte SQL ausführt, das DynamoDB nicht kann — JOINs, GROUP BY, Aggregationen — mit visueller Bearbeitung und einem KI-Agenten auf deinen eigenen Bedrock-Schlüsseln.

30 Tage kostenlos testen, keine Kreditkarte — danach der Kostenlos-Tarif ohne Zeitlimit.