DynamoDB UpdateItem en Node.js (AWS SDK v3)

El cliente de bajo nivel de v3 habla DynamoDB JSON en ambas direcciones, lo que significa que cada número que envías y cada número que recibes es una cadena. Eso no es una verruga; es la única forma de que un número de 38 dígitos de DynamoDB sobreviva a un lenguaje cuyo único tipo numérico es un double. También es donde están los bugs.

Código

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

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

const command = new UpdateItemCommand({
  TableName: 'Music',
  Key: {
    Artist: {S: 'Arturo Sandoval'},
    SongTitle: {S: 'Cubano Chant'}
  },
  UpdateExpression: 'SET #upd0 = :updValue0, #upd1 = :updValue1 ADD #upd2 :updValue2',
  ExpressionAttributeNames: {
    '#upd0': 'Genre',
    '#upd1': 'Year',
    '#upd2': 'Awards'
  },
  ExpressionAttributeValues: {
    ':updValue0': {S: 'Latin Jazz'},
    ':updValue1': {N: '1994'},
    ':updValue2': {N: '1'}
  },
  ReturnValues: 'ALL_NEW'
});

const response = await client.send(command);
console.log(response.Attributes); // the item after the update

Contra un Item que no tenía Genre ni Awards, response.Attributes vuelve como:

{"Artist":{"S":"Arturo Sandoval"},"Awards":{"N":"1"},"Genre":{"S":"Latin Jazz"},"Year":{"N":"1994"},"SongTitle":{"S":"Cubano Chant"}}

typeof response.Attributes.Awards.N es "string", así que response.Attributes.Awards.N + 1 se evalúa como "11". Nada lanza un error, nada avisa, y el número equivocado se cuela en tu siguiente escritura. Parsea en la frontera: Number(response.Attributes.Awards.N).

Explicación

  • La expresión es una cadena normal, y v3 no la va a comprobar. UpdateItemCommand valida la forma del objeto de entrada, nunca la gramática dentro de UpdateExpression, así que una errata es un viaje de ida y vuelta y un 400. La gramática está en expresiones de actualización; ADD #upd2 :updValue2 es el incremento atómico, y añadir ConditionExpression: 'attribute_exists(Artist)' hace que la llamada sea solo de actualización en vez de un upsert.

  • ReturnValues: 'UPDATED_NEW' suele ser el que quieres. La misma actualización devuelve {"Awards":{"N":"2"}} y nada más. ALL_NEW te manda de vuelta el Item entero en cada llamada, lo que en un Item gordo es ancho de banda que pagas por leer un solo contador.

  • $metadata es el canal fuera de banda de v3: {"httpStatusCode":200,"requestId":"...","attempts":1,"totalRetryDelay":0}. attempts es la respuesta honesta a "¿esto se reintentó?", que importa cuando razonas sobre si una escritura no idempotente se ejecutó dos veces.

  • ValidationException no es una clase que puedas capturar, solo un name que puedes comparar. Un alias que falta vuelve como err.name === 'ValidationException' con err.message puesto a Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: Year.

  • El document client es el otro intercambio. @aws-sdk/lib-dynamodb toma valores nativos de JS y hace unmarshal de la respuesta, a costa de esa seguridad de las cadenas. marshall({awards: 9007199254740993}) de @aws-sdk/util-dynamodb se niega de plano:

    Number 9007199254740992 is greater than Number.MAX_SAFE_INTEGER. Use NumberValue from @aws-sdk/lib-dynamodb.

    Mira bien el número de ese mensaje. Acaba en 2, no en el 3 que se escribió en el literal: JavaScript ya lo había redondeado antes de que el SDK lo viera siquiera. El cliente de bajo nivel de este fragmento no puede tener ese problema, porque {N: '9007199254740993'} es texto hasta llegar al cable.

Qué te entrega una condición fallida

Añade ReturnValuesOnConditionCheckFailure: 'ALL_OLD' a la entrada y el error lanzado lleva el Item que te ganó:

name: ConditionalCheckFailedException | message: "The conditional request failed" | http: 400
err.Item: {"Artist":{"S":"Arturo Sandoval"},"Awards":{"N":"2"},"Year":{"N":"1994"},"SongTitle":{"S":"Cubano Chant"},"Genre":{"S":"Latin Jazz"}}

err.Item es DynamoDB JSON en crudo, sea cual sea el cliente que lo lanzó, y es gratis. Sin él, la forma honesta de averiguar por qué falló una actualización con concurrencia optimista es un GetItem posterior que cuesta una lectura y puede estar desactualizado otra vez.

El conversor de DynamoDB JSON convierte esa carga en un objeto JS plano y al revés, que es la forma más rápida de montar un fixture a partir de un Item real. Para sacar ese Item de una tabla en vivo, para empezar, descarga DynoTable.

Guías relacionadas

Referencias

Verificado por última vez el 2026-07-28 contra la documentación oficial de AWS enlazada arriba.

Trabaja con DynamoDB sin la Consola

Un cliente de escritorio rápido para DynamoDB que ejecuta el SQL real que DynamoDB no puede — JOINs, GROUP BY, agregaciones — con edición visual y un agente de IA con tus propias claves de Bedrock.

Prueba gratuita de 30 días, sin tarjeta — después, el plan Free sin límite de tiempo.