Escrita condicional no DynamoDB em Node.js (AWS SDK v3)
A parte interessante de uma escrita condicional no AWS SDK v3 não é a ConditionExpression, que funciona igual em todo lugar e está coberta em expressões de condição do DynamoDB. É o caminho de falha: a v3 te entrega o item perdedor no erro lançado, se você o pediu, e não te dá nada se não pediu.
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;
}
}Explicação
- A checagem que falha é um erro lançado, não um campo de status. A v3 rejeita a promise, então o caminho da escrita e o caminho da corrida perdida são ramos diferentes.
err.name === 'ConditionalCheckFailedException'é o discriminador; qualquer outra coisa precisa ser relançada, que é para isso que serve oelseno bloco. Engula ocatchinteiro e você terá transformado silenciosamente um throttle em um no-op. ReturnValuesOnConditionCheckFailureé a única forma de ver quem te venceu. Sem ele, o erro carrega a mensagem e nada mais, e você volta para umGetItemque não precisava. A referência da API define seus valores válidos comoALL_OLD | NONEe confirma que ele não consome capacidade de leitura.err.Itemé um mapaAttributeValuebruto, com o mesmo formato daKeyque você enviou, e não JavaScript comum. Passe-o pelounmarshalldo@aws-sdk/util-dynamodbantes de compararVersioncom um número, ou você estará comparando com{N: '9'}.- A escrita que falhou ainda é cobrada. O Developer Guide é explícito ao dizer que uma condição que avalia como falsa ainda consome capacidade de escrita, dimensionada pelo maior entre o item antigo e o novo. Um laço de retry em uma chave quente é uma linha real na conta, então limite as tentativas.
- Todo nome no bloco está com alias (
#version→Version,#cond0→Artist) porque o Expression Builder que o gerou coloca alias incondicionalmente. Isso é mais pesado que o necessário aqui e nunca está errado, que é a troca que ele faz.
Lendo a cópia do perdedor a partir da exceção
Coloque o Version armazenado em 9 e rode o bloco, que espera 7. O DynamoDB Local 3.3.0 lança o erro, e o erro capturado carrega:
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' }
}Esse Version: 9 é o ponto todo. O retry pode voltar direto ao update com :expectedVersion em 9, sem leitura extra e sem uma janela na qual um terceiro escritor se meta entre o seu GetItem e o seu retry.
Apague ReturnValuesOnConditionCheckFailure do mesmo comando e rode de novo. Mesmo name, mesma message, mesmo 400, e err.Item é undefined. Nada te avisa: o parâmetro é opcional, a ausência dele não é um erro, e o código que lê err.Item simplesmente começa a registrar undefined em produção.
Repare também que um 400 aqui não significa uma requisição malformada. ValidationException e ConditionalCheckFailedException compartilham o código de status, e só um deles é um bug — por isso o ramo é feito em err.name e nunca no status.
Para ver uma condição ter sucesso e falhar contra os seus próprios dados, com a expressão escrita para você em vez de digitada, baixe o DynoTable.
Exemplos relacionados
- Escrita condicional no DynamoDB em Python — o mesmo bloqueio otimista com boto3.
- Escrita condicional no DynamoDB com a AWS CLI — o mesmo bloqueio otimista a partir do shell.
- PutItem do DynamoDB em Node.js — o put só-de-criação com
attribute_not_exists. - Expressões de condição do DynamoDB — cada função, com padrões de uso.
- Garantindo unicidade em múltiplos atributos — condições + transações combinadas.
- DynamoDB ConditionalCheckFailedException — quando a checagem que falha é esperada, e como tratá-la de forma barata.
Referências
- UpdateItem — Amazon DynamoDB API Reference
- Condition expressions — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
Verificado pela última vez em 2026-07-28 contra a documentação oficial da AWS vinculada acima.