DynamoDB UpdateItem en Go (AWS SDK v2)
UpdateItem es sencillo en Go; lo incómodo es que map[string]types.AttributeValue es un mapa de interfaces, así que tanto los valores que envías como los que recibes son punteros a una de nueve structs miembro. Esa única decisión de diseño explica casi toda la fricción de abajo, empezando por el hecho de que el fmt.Println de este fragmento no imprime tu Item.
Código
package main
import (
"context"
"fmt"
"log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)
func main() {
ctx := context.TODO()
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1"))
if err != nil {
log.Fatalf("load config: %v", err)
}
client := dynamodb.NewFromConfig(cfg)
out, err := client.UpdateItem(ctx, &dynamodb.UpdateItemInput{
TableName: aws.String("Music"),
Key: map[string]types.AttributeValue{
"Artist": &types.AttributeValueMemberS{Value: "Arturo Sandoval"},
"SongTitle": &types.AttributeValueMemberS{Value: "Cubano Chant"},
},
UpdateExpression: aws.String("SET #upd0 = :updValue0, #upd1 = :updValue1 ADD #upd2 :updValue2"),
ExpressionAttributeNames: map[string]string{
"#upd0": "Genre",
"#upd1": "Year",
"#upd2": "Awards",
},
ExpressionAttributeValues: map[string]types.AttributeValue{
":updValue0": &types.AttributeValueMemberS{Value: "Latin Jazz"},
":updValue1": &types.AttributeValueMemberN{Value: "1994"},
":updValue2": &types.AttributeValueMemberN{Value: "1"},
},
ReturnValues: types.ReturnValueAllNew,
})
if err != nil {
log.Fatalf("update item: %v", err)
}
fmt.Println(out.Attributes) // the item after the update
}Explicación
AttributeValueMemberN.Valuees unstring, no un tipo numérico, yattributevalue.Marshallo mantiene así:int64(9007199254740993)se marshaliza exactamente como"9007199254740993". Los números de DynamoDB llevan 38 dígitos de precisión, más que cualquier float de Go, así que el SDK nunca convierte. El parseo lo haces tú, en el borde, a propósito.ReturnValues: types.ReturnValueAllNew— el valor de la constante es el literal"ALL_NEW".types.ReturnValueAllNew.Values()lista las cinco (NONE,ALL_OLD,UPDATED_OLD,ALL_NEW,UPDATED_NEW) si prefieres leerlas del tipo en vez de la documentación.UpdateExpressiones un*stringque construyes tú, o que delegas en el paqueteexpressionde abajo. En cualquier caso DynamoDB es el único que parsea:ADD #upd2 :updValue2da el incremento atómico,attribute_exists(Artist)en unaConditionExpressionhace que la llamada sea solo de actualización en vez de un upsert, y la gramática de las cláusulas está en expresiones de actualización.ValidationExceptionno tiene tipo en Go.types/errors.godefine 35 structs de error, incluidosConditionalCheckFailedException,TransactionCanceledException,ProvisionedThroughputExceededExceptionyTransactionConflictException. Los fallos de validación no están entre ellos, así que una expresión mala aflora como unsmithy.APIErrorgenérico que solo puedes reconocer por la cadena:operation error DynamoDB: UpdateItem, https response error StatusCode: 400, RequestID: 702d67f1-4f15-43dc-b3e9-cea691878801, api error ValidationException: Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: YearFíjate en el prefijo
api error, que laConditionalCheckFailedExceptionmodelada de abajo no lleva. Su presencia es una señal bastante buena de queerrors.Ascontra un tipo concreto no te va a servir aquí.Una condición fallida, en cambio, sí es un tipo real.
var ccf *types.ConditionalCheckFailedException; errors.As(err, &ccf)coincide, y conReturnValuesOnConditionCheckFailure: types.ReturnValuesOnConditionCheckFailureAllOlden la entrada,ccf.Itemllega relleno con el Item tal como estaba de verdad.ccf.ErrorMessage()es el escuetoThe conditional request failed, sin el preámbulo de transporte queerr.Error()antepone.
Leer el resultado de vuelta
fmt.Println(out.Attributes) sobre un mapa de interfaces imprime direcciones:
map[Artist:0x1c8cf589e060 Awards:0x1c8cf589e078 Genre:0x1c8cf589e090 SongTitle:0x1c8cf589e0c0 Year:0x1c8cf589e0a8]Dos salidas. Hacer unmarshal a una struct, que es lo que debería hacer la mayoría del código:
var song struct {
Artist string
Genre string
Year int
Awards int
}
err = attributevalue.UnmarshalMap(out.Attributes, &song)
// {Artist:Arturo Sandoval Genre:Latin Jazz Year:1994 Awards:1}O afirmar el tipo del único miembro que te importa, recordando que .Value es una cadena:
n, ok := out.Attributes["Awards"].(*types.AttributeValueMemberN)
if ok {
awards, _ := strconv.Atoi(n.Value) // "1" -> 1
fmt.Println(awards)
}Deja que la escriba el paquete expression
Go es el único SDK de este sitio que te genera la UpdateExpression. feature/dynamodb/expression compone las cláusulas y los dos mapas:
upd := expression.Set(expression.Name("Genre"), expression.Value("Latin Jazz")).
Set(expression.Name("Year"), expression.Value(1994)).
Add(expression.Name("Awards"), expression.Value(1))
expr, _ := expression.NewBuilder().WithUpdate(upd).Build()Lo que sale no es lo que escribiste:
UpdateExpression: ADD #0 :0
SET #1 = :1, #2 = :2
Names: map[#0:Awards #1:Genre #2:Year]El builder reordenó las cláusulas, las separó con un salto de línea y numeró él mismo los marcadores, así que #0 es Awards y no el primer nombre que mencionaste. A nadie aguas abajo le importa, pero las cadenas no son estables entre ediciones, lo que las convierte en algo malo sobre lo que aseverar en tests. Pasa expr.Update(), expr.Names() y expr.Values() directos a UpdateItemInput y no los mires nunca.
La ventaja es que aliasea todos los nombres, así que las palabras reservadas dejan de ser una clase de bug que puedes llevarte a producción. Si escribes la expresión a mano, pasa antes los nombres de atributo por el comprobador de palabras reservadas de DynamoDB — la lista de AWS tiene 573 entradas y Year, Name y Status están todas en ella. Y si prefieres mirar un Item como datos en vez de como un mapa de punteros a interfaces, descarga DynoTable.
Ejemplos relacionados
- DynamoDB UpdateItem en Java — la misma actualización con AWS SDK for Java 2.x.
- DynamoDB PutItem en Go — reemplaza el Item entero en su lugar.
- Expresiones de actualización de DynamoDB —
SET,ADD,REMOVE,DELETEy sus modismos. - Entender ReturnValues — qué te da cada opción de
ReturnValues. - "Attribute name is a reserved keyword" — por qué el mapa de alias de aquí no es opcional.
- Errores de sintaxis "Invalid UpdateExpression" — los fallos habituales de sintaxis de SET/ADD, descifrados.
Referencias
- UpdateItem — Amazon DynamoDB API Reference
- Use UpdateItem with an AWS SDK or CLI — Amazon DynamoDB Developer Guide
- dynamodb package — AWS SDK for Go v2 (pkg.go.dev)
- dynamodb/types package — AWS SDK for Go v2 (pkg.go.dev)
- expression package — AWS SDK for Go v2 (pkg.go.dev)
- Update expressions — Amazon DynamoDB Developer Guide
Verificado por última vez el 2026-07-28 contra la documentación oficial de AWS enlazada arriba.