PutItem do DynamoDB em Go (AWS SDK v2)
O PutItem grava um item inteiro e substitui qualquer item existente com a mesma chave primária (ações baseadas em item explica como isso difere do UpdateItem). No AWS SDK for Go v2 a parte interessante não é a chamada, e sim no que os seus valores Go se transformam no caminho de saída.
Código
package main
import (
"context"
"errors"
"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)
_, err = client.PutItem(ctx, &dynamodb.PutItemInput{
TableName: aws.String("Music"),
Item: map[string]types.AttributeValue{
"Artist": &types.AttributeValueMemberS{Value: "Arturo Sandoval"},
"SongTitle": &types.AttributeValueMemberS{Value: "Cubano Chant"},
"AlbumTitle": &types.AttributeValueMemberS{Value: "Danzon"},
"Year": &types.AttributeValueMemberN{Value: "1994"},
"Awards": &types.AttributeValueMemberN{Value: "0"},
},
ConditionExpression: aws.String("attribute_not_exists(#cond0) AND attribute_not_exists(#cond1)"),
ExpressionAttributeNames: map[string]string{
"#cond0": "Artist",
"#cond1": "SongTitle",
},
})
if err != nil {
var ccf *types.ConditionalCheckFailedException
if errors.As(err, &ccf) {
fmt.Println("A song with that key already exists — not overwritten")
return
}
log.Fatalf("put item: %v", err)
}
fmt.Println("Song written")
}Explicação
O attributevalue.MarshalMap é o atalho, e ele tem opiniões. Entregar uma struct ao github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue em vez de montar à mão o map[string]types.AttributeValue acima é o movimento normal. Isto é o que ele de fato produziu para uma struct com um time.Time, um campo string intocado e um *int nil:
Artist => &types.AttributeValueMemberS{Value:"Arturo Sandoval"}
SongTitle => &types.AttributeValueMemberS{Value:"Cubano Chant"}
Released => &types.AttributeValueMemberS{Value:"1994-01-01T00:00:00Z"}
Notes => &types.AttributeValueMemberS{Value:""}
Rating => &types.AttributeValueMemberNULL{Value:true}Três coisas para tirar daí. time.Time vira uma string RFC 3339, não um número Unix, então uma chave de classificação de timestamp ordena lexicograficamente e só vai se comportar se todo valor estiver preenchido com zeros à esquerda e no mesmo fuso. Uma string intocada vira um atributo de string vazia de verdade, em vez de ficar de fora. E um ponteiro nil vira NULL, que é um atributo que existe.
Um atributo NULL derrota o attribute_not_exists. Essa é a que custa uma tarde. Grave um item cujo Rating veio de um *int nil, depois proteja a próxima escrita com attribute_not_exists(Rating) e ela falha:
ConditionalCheckFailedException: The conditional request failedO DynamoDB está certo: o atributo está lá, contendo NULL. A correção é a struct tag, dynamodbav:"Rating,omitempty", que descarta o campo em vez de anulá-lo. A mesma tag impede o atributo de string vazia, o que importa porque essas quebram índices esparsos e, em um atributo de chave, são rejeitadas de imediato:
ValidationException: One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an empty string value. Key: Artisterrors.As, e nunca comparação de strings. O SDK de Go envolve todo erro de serviço no erro de operação do smithy, então a string que você recebe de err.Error() não é a mensagem que o DynamoDB enviou:
operation error DynamoDB: PutItem, https response error StatusCode: 400, RequestID: 6886fce0-b246-4762-8a8b-0c6dab813040, ConditionalCheckFailedException: The conditional request failedO errors.As(err, &ccf) desembrulha isso e retornou true acima; o ccf.ErrorMessage() então devolve o The conditional request failed cru. Uma checagem com strings.Contains na string externa funciona hoje e quebra no dia em que o SDK mudar o formato do seu wrapper.
O erro tipado pode carregar o item que te bloqueou. Defina ReturnValuesOnConditionCheckFailure: types.ReturnValuesOnConditionCheckFailureAllOld na entrada e o ccf.Item volta preenchido: cinco atributos na execução acima, incluindo Year como &types.AttributeValueMemberN{Value:"1994"}. Isso poupa a ida e volta de leitura-após-falha que a maioria dos laços de retry faz na mão. O ReturnValues: types.ReturnValueAllOld é o equivalente no caminho de sucesso (ReturnValues).
Números são strings, e isso não é uma esquisitice do Go. AttributeValueMemberN{Value: "1994"} parece errado para quem vem de uma linguagem tipada, mas o tipo N do DynamoDB é um decimal transportado como texto justamente para que nada precise passar por um float64 na ida e na volta. strconv.FormatInt e strconv.FormatFloat fazem a conversão; o attributevalue faz isso por você.
O que você marshalla é o que você paga. Um put de um item de ~15 KB reportou "CapacityUnits": 15 com o ReturnConsumedCapacity. As escritas arredondam para cima a cada 1 KB, então um campo blob acidental, ou um MarshalMap que emitiu atributos que você queria descartar, aparece direto na conta.
Faça isso visualmente
Como o MarshalMap decide o que de fato entra no item, vale saber quanto esse item pesa. A calculadora de tamanho de item do DynamoDB gratuita recebe o JSON marshalado e devolve o tamanho em bytes e as unidades de escrita para as quais ele arredonda.
Para gravar e editar itens contra as suas próprias tabelas — um formulário por atributo, seletores de tipo e o resultado copiado de volta como Go — baixe o DynoTable.
Exemplos relacionados
- PutItem do DynamoDB em Java — a mesma escrita condicional com o AWS SDK for Java 2.x, onde um valor não definido falha do jeito oposto.
- UpdateItem do DynamoDB em Go — altere atributos específicos em vez de substituir o item.
- Expressões de condição do DynamoDB —
attribute_not_exists, bloqueio otimista e mais. - DynamoDB ConditionalCheckFailedException — o que a condição só-de-criação lança quando o item já existe.
- DynamoDB ValidationException — o pega-tudo para um item ou expressão malformados.
Referências
- PutItem — Amazon DynamoDB API Reference
- Use PutItem with an AWS SDK or CLI — Amazon DynamoDB Developer Guide
- dynamodb package — AWS SDK for Go v2 (pkg.go.dev)
- attributevalue package — AWS SDK for Go v2 (pkg.go.dev)
- Handling errors — AWS SDK for Go v2 Developer Guide
- Condition expressions — Amazon DynamoDB Developer Guide
Reproduzido em 2026-07-28 no go1.26.5 com aws-sdk-go-v2/service/dynamodb v1.62.1 e feature/dynamodb/attributevalue v1.20.55, contra o DynamoDB Local (amazon/dynamodb-local) na porta 9000. Os valores marshalados, as strings de erro e a leitura de capacidade são saída capturada, copiada literalmente.