Écriture conditionnelle DynamoDB en Node.js (AWS SDK v3)

Ce qui est intéressant dans une écriture conditionnelle avec AWS SDK v3, ce n'est pas la ConditionExpression, qui fonctionne pareil partout et est couverte dans les expressions de condition DynamoDB. C'est le chemin d'échec : v3 te remet l'élément perdant sur l'erreur levée, si tu l'as demandé, et ne te donne rien si tu ne l'as pas fait.

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;
  }
}

Explication

  • La vérification échouée est une erreur levée, pas un champ de statut. v3 rejette la promesse, donc le chemin d'écriture et le chemin « course perdue » sont deux branches distinctes. err.name === 'ConditionalCheckFailedException' est le discriminant ; tout le reste doit être relancé, et c'est à ça que sert le else du bloc. Avale tout le catch et tu auras silencieusement transformé un throttle en no-op.
  • ReturnValuesOnConditionCheckFailure est le seul moyen de voir qui t'a doublé. Sans lui, l'erreur ne porte que le message et rien d'autre, et te voilà de retour sur un GetItem dont tu n'avais pas besoin. La référence de l'API fixe ses valeurs valides à ALL_OLD | NONE et confirme qu'il ne consomme aucune capacité de lecture.
  • err.Item est une map AttributeValue brute, de la même forme que la Key que tu as envoyée, pas du JavaScript ordinaire. Passe-la par unmarshall de @aws-sdk/util-dynamodb avant de comparer Version à un nombre, sinon tu compareras à {N: '9'}.
  • L'écriture échouée est quand même facturée. Le Developer Guide est explicite : une condition qui s'évalue à faux consomme quand même de la capacité d'écriture, dimensionnée sur le plus gros de l'ancien et du nouvel élément. Une boucle de reprise sur une clé chaude est une vraie ligne sur la facture, alors plafonne les tentatives.
  • Tous les noms du bloc sont aliasés (#versionVersion, #cond0Artist) parce que l'Expression Builder qui l'a généré aliase systématiquement. C'est plus lourd que nécessaire ici et jamais faux, c'est le compromis qu'il fait.

Lire la copie du perdant sur l'exception

Mets la Version stockée à 9 et lance le bloc, qui en attend 7. DynamoDB Local 3.3.0 lève une exception, et l'erreur attrapée porte :

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' }
             }

Ce Version: 9 est tout l'intérêt. La reprise peut repasser directement par la mise à jour avec :expectedVersion à 9, sans lecture supplémentaire et sans fenêtre où un troisième écrivain se glisserait entre ton GetItem et ta reprise.

Supprime ReturnValuesOnConditionCheckFailure de la même commande et relance-la. Même name, même message, même 400, et err.Item vaut undefined. Rien ne t'avertit : le paramètre est optionnel, son absence n'est pas une erreur, et le code qui lit err.Item se met simplement à journaliser undefined en production.

Note aussi qu'un 400 ici ne signifie pas une requête malformée. ValidationException et ConditionalCheckFailedException partagent le code de statut, et une seule des deux est un bug : c'est pour ça que la branche porte sur err.name et jamais sur le statut.

Pour voir une condition réussir puis échouer sur tes propres données, avec l'expression écrite pour toi plutôt que tapée, télécharge DynoTable.

Exemples liés

Références

Dernière vérification le 2026-07-28 par rapport à la documentation officielle AWS liée ci-dessus.

Travaille avec DynamoDB sans la Console

Un client de bureau rapide pour DynamoDB qui exécute le vrai SQL que DynamoDB ne peut pas — JOINs, GROUP BY, agrégations — avec édition visuelle et un agent IA sur tes propres clés Bedrock.

Essai gratuit de 30 jours, sans carte bancaire — ensuite la formule Gratuit, sans limite de durée.