DynamoDB GetItem in Go (AWS SDK v2)

In AWS SDK for Go v2 an item is a map[string]types.AttributeValue, and types.AttributeValue is a sealed interface: the only implementations are the ten AttributeValueMember* structs the SDK ships. Writing a key with them is easy. Reading values back out is where Go diverges from every other SDK.

client.GetItem still wants the full primary key, same as everywhere else.

Code

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.GetItem(ctx, &dynamodb.GetItemInput{
		TableName: aws.String("Music"),
		Key: map[string]types.AttributeValue{
			"Artist":    &types.AttributeValueMemberS{Value: "Arturo Sandoval"},
			"SongTitle": &types.AttributeValueMemberS{Value: "Cubano Chant"},
		},
		ProjectionExpression: aws.String("#proj0, #proj1, #proj2, #proj3"),
		ExpressionAttributeNames: map[string]string{
			"#proj0": "Artist",
			"#proj1": "SongTitle",
			"#proj2": "AlbumTitle",
			"#proj3": "Year",
		},
	})
	if err != nil {
		log.Fatalf("get item: %v", err)
	}

	if out.Item == nil {
		fmt.Println("Item not found")
		return
	}
	fmt.Println(out.Item)
}

Explanation

  • Scalars are pointersTableName, ProjectionExpression and ConsistentRead are *string and *bool so the SDK can tell "unset" from the zero value. aws.String, aws.Bool and aws.Int32 exist for that and nothing else.
  • Numbers arrive as stringsAttributeValueMemberN.Value is a string, because DynamoDB sends numbers as strings on the wire. Reading Year means out.Item["Year"].(*types.AttributeValueMemberN) and then strconv.Atoi. Use the comma-ok form of the assertion; the bare form panics when the attribute is missing or a different type.
  • attributevalue.UnmarshalMap is the way out — from github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue, it maps the whole item onto a struct with dynamodbav tags and does the string-to-number conversion. Worth reaching for the moment an item has more than a couple of attributes.
  • out.Item is nil on a miss, and err is nil too. len(out.Item) == 0 covers both nil and empty if you do not need to distinguish them.
  • Match errors with errors.As — Go v2 wraps service faults in a Smithy operation error, so err == … and string matching on err.Error() both silently fail. Declare var nf *types.ResourceNotFoundException and pass its address.
  • ConsistentRead: aws.Bool(true) doubles the read cost and is rejected on a GSI. The #proj aliases above are not decoration either: Year is a reserved word, and a projection naming it directly is refused.

Do it visually

Translating between the AttributeValueMember* form and plain JSON is a constant tax in Go. The DynamoDB JSON converter does that round trip in the browser when you only need to read or paste one item.

DynoTable shows items as ordinary rows instead, and exports the query behind the grid as a Go program built on these same SDK v2 types. Download DynoTable.

References

Last verified 2026-07-28 against the official AWS documentation linked above.

Work with DynamoDB without the Console

A fast DynamoDB desktop client that runs the real SQL DynamoDB can’t — JOINs, GROUP BY, aggregates — with visual editing and an AI agent on your own Bedrock keys.

Free 30-day trial, no credit card — then the Free plan with no time limit.