DynamoDB PutItem in Go (AWS SDK v2)
PutItem schreibt ein komplettes Item und ersetzt jedes vorhandene Item mit demselben Primary Key (Item-basierte Aktionen behandelt, wie sich das von UpdateItem unterscheidet). Im AWS SDK für Go v2 ist der interessante Teil nicht der Aufruf, sondern das, was aus deinen Go-Werten auf dem Weg nach draußen wird.
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")
}Erklärung
attributevalue.MarshalMap ist die Abkürzung, und sie hat Meinungen. Ein Struct an github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue zu geben, statt die map[string]types.AttributeValue oben von Hand zu bauen, ist der normale Weg. Das hier hat es tatsächlich für ein Struct mit einer time.Time, einem unberührten string-Feld und einem nil-*int produziert:
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}Drei Dinge sind daraus mitzunehmen. time.Time wird zu einem RFC-3339-String, nicht zu einer Unix-Zahl — ein Timestamp-Sort-Key sortiert also lexikografisch und verhält sich nur dann korrekt, wenn jeder Wert nullgepolstert und in derselben Zeitzone ist. Ein unberührter String wird zu einem echten Attribut mit leerem String, statt weggelassen zu werden. Und ein nil-Pointer wird zu NULL, also einem Attribut, das existiert.
Ein NULL-Attribut hebelt attribute_not_exists aus. Das ist das, was einen Nachmittag kostet. Schreib ein Item, dessen Rating aus einem nil-*int kam, sichere dann den nächsten Write mit attribute_not_exists(Rating) ab — und er scheitert:
ConditionalCheckFailedException: The conditional request failedDynamoDB hat recht: Das Attribut ist da und hält NULL. Die Lösung ist das Struct-Tag dynamodbav:"Rating,omitempty", das das Feld weglässt, statt es auf null zu setzen. Dasselbe Tag verhindert das Attribut mit leerem String, was zählt, weil solche Werte Sparse Indexes kaputtmachen und in einem Key-Attribut rundheraus abgelehnt werden:
ValidationException: One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an empty string value. Key: Artisterrors.As, und niemals String-Matching. Das Go-SDK verpackt jeden Service-Fehler in Smithys Operation-Error, der String aus err.Error() ist also nicht die Meldung, die DynamoDB geschickt hat:
operation error DynamoDB: PutItem, https response error StatusCode: 400, RequestID: 6886fce0-b246-4762-8a8b-0c6dab813040, ConditionalCheckFailedException: The conditional request failederrors.As(err, &ccf) packt ihn aus und lieferte oben true; ccf.ErrorMessage() gibt dann das nackte The conditional request failed. Eine strings.Contains-Prüfung auf dem äußeren String funktioniert heute und geht an dem Tag kaputt, an dem das SDK sein Wrapper-Format ändert.
Der typisierte Fehler kann das Item mitbringen, das dich blockiert hat. Setze ReturnValuesOnConditionCheckFailure: types.ReturnValuesOnConditionCheckFailureAllOld im Input, und ccf.Item kommt gefüllt zurück: im obigen Lauf fünf Attribute, darunter Year als &types.AttributeValueMemberN{Value:"1994"}. Das spart den Read-nach-Fehlschlag-Roundtrip, den die meisten Retry-Schleifen von Hand machen. ReturnValues: types.ReturnValueAllOld ist das Äquivalent für den Erfolgspfad (ReturnValues).
Zahlen sind Strings, und das ist keine Go-Eigenart. AttributeValueMemberN{Value: "1994"} sieht für jeden falsch aus, der aus einer typisierten Sprache kommt, aber DynamoDBs N-Typ ist eine Dezimalzahl, die als Text transportiert wird — genau damit nichts über ein float64 hin und zurück muss. strconv.FormatInt und strconv.FormatFloat sind die Umwandlung; attributevalue macht es für dich.
Was du marshallst, ist das, wofür du zahlst. Ein Put eines ~15 KB großen Items meldete mit ReturnConsumedCapacity "CapacityUnits": 15. Writes runden pro 1 KB auf — ein versehentliches Blob-Feld oder ein MarshalMap, das Attribute ausgegeben hat, die du weglassen wolltest, taucht also direkt auf der Rechnung auf.
Mach es visuell
Da MarshalMap entscheidet, was tatsächlich im Item landet, lohnt es sich zu wissen, wie schwer dieses Item ist. Der kostenlose DynamoDB-Item-Size-Rechner nimmt das marshallte JSON und liefert die Byte-Größe und die Schreibeinheiten, auf die sie aufrundet.
Um Items gegen deine eigenen Tabellen zu schreiben und zu bearbeiten — ein Formular pro Attribut, Typ-Auswahl, das Ergebnis wieder als Go herauskopieren — lade DynoTable herunter.
Verwandte Beispiele
- DynamoDB PutItem in Java — derselbe bedingte Write mit AWS SDK für Java 2.x, wo ein nicht gesetzter Wert genau andersherum scheitert.
- DynamoDB UpdateItem in Go — bestimmte Attribute ändern, statt das Item zu ersetzen.
- DynamoDB Condition Expressions —
attribute_not_exists, optimistisches Locking und mehr. - DynamoDB ConditionalCheckFailedException — was die Nur-Anlegen-Bedingung wirft, wenn das Item schon existiert.
- DynamoDB ValidationException — der Sammelfehler für ein fehlerhaftes Item oder eine fehlerhafte Expression.
Referenzen
- 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
Am 2026-07-28 auf go1.26.5 mit aws-sdk-go-v2/service/dynamodb v1.62.1 und feature/dynamodb/attributevalue v1.20.55 gegen DynamoDB Local (amazon/dynamodb-local) auf Port 9000 reproduziert. Die marshallten Werte, die Fehler-Strings und der Kapazitätswert sind aufgezeichnete Ausgabe, wortgetreu kopiert.