É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 leelsedu bloc. Avale tout lecatchet tu auras silencieusement transformé un throttle en no-op. ReturnValuesOnConditionCheckFailureest 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 unGetItemdont tu n'avais pas besoin. La référence de l'API fixe ses valeurs valides àALL_OLD | NONEet confirme qu'il ne consomme aucune capacité de lecture.err.Itemest une mapAttributeValuebrute, de la même forme que laKeyque tu as envoyée, pas du JavaScript ordinaire. Passe-la parunmarshallde@aws-sdk/util-dynamodbavant de comparerVersionà 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 (
#version→Version,#cond0→Artist) 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
- Écriture conditionnelle DynamoDB en Python — le même verrou optimiste avec boto3.
- Écriture conditionnelle DynamoDB avec l'AWS CLI — le même verrou optimiste depuis le shell.
- DynamoDB PutItem en Node.js — le put
attribute_not_existsen création seule. - Les expressions de condition DynamoDB — chaque fonction, avec des motifs.
- Imposer l'unicité sur plusieurs attributs — conditions et transactions combinées.
- DynamoDB ConditionalCheckFailedException — quand la vérification échouée est attendue, et comment la gérer à moindre coût.
Références
- UpdateItem — Amazon DynamoDB API Reference
- Condition expressions — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
Dernière vérification le 2026-07-28 par rapport à la documentation officielle AWS liée ci-dessus.