DynamoDB PutItem en Go (AWS SDK v2)

PutItem écrit un élément entier et remplace tout élément existant portant la même clé primaire (les actions par élément expliquent en quoi ça diffère d'UpdateItem). Dans AWS SDK for Go v2, l'intéressant n'est pas l'appel, c'est ce que deviennent tes valeurs Go en chemin.

Code

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")
}

Explication

attributevalue.MarshalMap est le raccourci, et il a ses opinions. Passer une struct à github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue au lieu de construire à la main la map[string]types.AttributeValue ci-dessus est le réflexe normal. Voici ce qu'il a réellement produit pour une struct avec un time.Time, un champ string jamais touché et un *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}

Trois choses à en retenir. Un time.Time devient une chaîne RFC 3339, pas un nombre Unix : une clé de tri temporelle se trie donc lexicographiquement et ne se comportera correctement que si toutes les valeurs sont complétées par des zéros et dans le même fuseau. Une chaîne jamais touchée devient un vrai attribut chaîne vide au lieu d'être omise. Et un pointeur nil devient NULL, c'est-à-dire un attribut qui existe.

Un attribut NULL met en échec attribute_not_exists. C'est celui qui coûte un après-midi. Écris un élément dont le Rating venait d'un *int nil, puis protège l'écriture suivante avec attribute_not_exists(Rating) : elle échoue.

ConditionalCheckFailedException: The conditional request failed

DynamoDB a raison : l'attribut est là, il contient NULL. La correction est le tag de struct, dynamodbav:"Rating,omitempty", qui supprime le champ au lieu de le mettre à null. Le même tag évite l'attribut chaîne vide, ce qui compte parce que ceux-là cassent les index creux et, dans un attribut de clé, sont rejetés d'emblée :

ValidationException: One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an empty string value. Key: Artist

errors.As, et jamais de comparaison de chaînes. Le SDK Go enveloppe chaque erreur de service dans l'erreur d'opération de smithy, donc la chaîne que te donne err.Error() n'est pas le message envoyé par DynamoDB :

operation error DynamoDB: PutItem, https response error StatusCode: 400, RequestID: 6886fce0-b246-4762-8a8b-0c6dab813040, ConditionalCheckFailedException: The conditional request failed

errors.As(err, &ccf) la déballe et a renvoyé true ci-dessus ; ccf.ErrorMessage() donne alors le simple The conditional request failed. Un test strings.Contains sur la chaîne extérieure fonctionne aujourd'hui et casse le jour où le SDK change le format de son enveloppe.

L'erreur typée peut porter l'élément qui t'a bloqué. Mets ReturnValuesOnConditionCheckFailure: types.ReturnValuesOnConditionCheckFailureAllOld sur l'entrée et ccf.Item revient rempli : cinq attributs dans l'exécution ci-dessus, dont Year sous la forme &types.AttributeValueMemberN{Value:"1994"}. Ça t'épargne l'aller-retour de relecture après échec que la plupart des boucles de reprise font à la main. ReturnValues: types.ReturnValueAllOld en est l'équivalent sur le chemin de succès (ReturnValues).

Les nombres sont des chaînes, et ce n'est pas une bizarrerie de Go. AttributeValueMemberN{Value: "1994"} a l'air faux à qui vient d'un langage typé, mais le type N de DynamoDB est un décimal transporté sous forme de texte précisément pour que rien n'ait à passer par un float64. strconv.FormatInt et strconv.FormatFloat font la conversion ; attributevalue la fait pour toi.

Ce que tu marshalles est ce que tu paies. Un put d'un élément d'environ 15 Ko a rapporté "CapacityUnits": 15 avec ReturnConsumedCapacity. Les écritures sont arrondies au Ko supérieur, donc un champ blob accidentel, ou un MarshalMap qui a émis des attributs que tu voulais écarter, se retrouve directement sur la facture.

Le faire visuellement

Puisque MarshalMap décide de ce qui atterrit réellement dans l'élément, autant savoir ce que pèse cet élément. Le calculateur de taille d'élément DynamoDB gratuit prend le JSON marshallé et renvoie la taille en octets et les unités d'écriture auxquelles elle est arrondie.

Pour écrire et modifier des éléments sur tes propres tables — un formulaire par attribut, des sélecteurs de type, le résultat recopiable en Go — télécharge DynoTable.

Exemples liés

Références

Reproduit le 2026-07-28 sur go1.26.5 avec aws-sdk-go-v2/service/dynamodb v1.62.1 et feature/dynamodb/attributevalue v1.20.55, contre DynamoDB Local (amazon/dynamodb-local) sur le port 9000. Les valeurs marshallées, les chaînes d'erreur et la mesure de capacité sont des sorties capturées, reproduites telles quelles.

Travaille avec DynamoDB sans la Console

Un client de bureau rapide pour DynamoDB qui exécute le vrai SQL que DynamoDB ne peut pas — JOINs, GROUP BY, agrégations — avec édition visuelle et un agent IA sur tes propres clés Bedrock.

Essai gratuit de 30 jours, sans carte bancaire — ensuite la formule Gratuit, sans limite de durée.