Escritura condicional en DynamoDB en Node.js (AWS SDK v3)
Lo interesante de una escritura condicional en el AWS SDK v3 no es la ConditionExpression, que funciona igual en todas partes y está cubierta en expresiones de condición de DynamoDB. Es el camino del fallo: v3 te entrega dentro del error lanzado el Item de la carrera que perdiste, si lo pediste, y no te da nada si no lo hiciste.
Código
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;
}
}Explicación
- La comprobación fallida es un error lanzado, no un campo de estado. v3 rechaza la promesa, así que el camino de la escritura y el de la carrera perdida son ramas distintas.
err.name === 'ConditionalCheckFailedException'es el discriminador; cualquier otra cosa hay que relanzarla, y para eso está elelsedel bloque. Si te tragas elcatchentero, has convertido en silencio un throttling en una operación sin efecto. ReturnValuesOnConditionCheckFailurees la única forma de ver quién te ganó. Sin él, el error lleva el mensaje y nada más, y vuelves a unGetItemque no necesitabas. La referencia de la API fija sus valores válidos enALL_OLD | NONEy confirma que no consume capacidad de lectura.err.Itemes un mapaAttributeValueen crudo, con la misma forma que laKeyque enviaste, no JavaScript plano. Pásalo porunmarshallde@aws-sdk/util-dynamodbantes de compararVersioncon un número, o estarás comparando contra{N: '9'}.- La escritura fallida también se factura. La Developer Guide es explícita: una condición que se evalúa como falsa consume capacidad de escritura igualmente, dimensionada según el mayor entre el Item antiguo y el nuevo. Un bucle de reintento sobre una clave caliente es una línea real en la factura, así que limita los intentos.
- Todos los nombres del bloque están aliaseados (
#version→Version,#cond0→Artist) porque el Expression Builder que lo generó aliasea sin condiciones. Aquí es más pesado de lo necesario y nunca es incorrecto: ese es el intercambio que hace.
Sacar de la excepción la copia del que perdió
Pon el Version almacenado a 9 y ejecuta el bloque, que espera 7. DynamoDB Local 3.3.0 lanza el error, y el error capturado lleva:
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' }
}Ese Version: 9 es la clave de todo. El reintento puede volver directo a la actualización con :expectedVersion puesto a 9, sin lectura extra y sin ninguna ventana por la que un tercer escritor se cuele entre tu GetItem y tu reintento.
Borra ReturnValuesOnConditionCheckFailure de ese mismo comando y vuelve a ejecutarlo. El mismo name, el mismo message, el mismo 400, y err.Item es undefined. Nada te avisa: el parámetro es opcional, su ausencia no es un error, y el código que lee err.Item simplemente empieza a registrar undefined en producción.
Fíjate también en que un 400 aquí no significa una petición mal formada. ValidationException y ConditionalCheckFailedException comparten el código de estado, y solo uno de los dos es un bug: por eso la rama va sobre err.name y nunca sobre el estado.
Para ver una condición tener éxito y fallar contra tus propios datos, con la expresión escrita por ti en vez de tecleada, descarga DynoTable.
Ejemplos relacionados
- Escritura condicional en DynamoDB en Python — el mismo bloqueo optimista con boto3.
- Escritura condicional en DynamoDB con la AWS CLI — el mismo bloqueo optimista desde la shell.
- DynamoDB PutItem en Node.js — el put de solo creación con
attribute_not_exists. - Expresiones de condición de DynamoDB — todas las funciones, con patrones.
- Forzar unicidad en varios atributos — condiciones y transacciones combinadas.
- DynamoDB ConditionalCheckFailedException — cuándo la comprobación fallida es esperada y cómo manejarla barato.
Referencias
- UpdateItem — Amazon DynamoDB API Reference
- Condition expressions — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
Verificado por última vez el 2026-07-28 contra la documentación oficial de AWS enlazada arriba.