Writing DynamoDB code by hand? Dynobase turns supported data operations into application code or scripts, and generates CDK, CloudFormation or Terraform for an existing table. Code generation in Dynobase
Use the AWS SDK for Go v2 to query DynamoDB with dynamodb.NewFromConfig, typed request structs and context-aware methods. Use attributevalue to convert Go structs to DynamoDB items, expression to build expressions, and Query or Scan paginators to read every page. These examples require Go 1.24 or later and cover table management, CRUD, batches, indexes and transactions.
Start a Go module and add the SDK packages. Each SDK module has its own version; the v2 in the import path identifies the SDK generation.
go mod init example.com/dynamodb-guide
go get github.com/aws/aws-sdk-go-v2/config \
github.com/aws/aws-sdk-go-v2/credentials \
github.com/aws/aws-sdk-go-v2/service/dynamodb \
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue \
github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression
Put this setup in main.go. The remaining Go blocks are functions to add to the same file, outside main. The import block covers the complete collection; remove imports for functions you do not copy. Call only the operations you intend to run. The default main creates a client without writing data.
Credentials come from the SDK's default chain, including a configured profile or the runtime's IAM role. Keep real credentials out of source code. Set AWS_PROFILE for local development and use the Region where your table exists.
package main
import("context""errors""fmt""math/rand/v2""time""github.com/aws/aws-sdk-go-v2/aws""github.com/aws/aws-sdk-go-v2/config""github.com/aws/aws-sdk-go-v2/credentials""github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue""github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression""github.com/aws/aws-sdk-go-v2/service/dynamodb""github.com/aws/aws-sdk-go-v2/service/dynamodb/types")const tableName ="my-table"type Item struct{
ID string`dynamodbav:"id"`
Date int`dynamodbav:"date"`
FirstName string`dynamodbav:"firstName"`
Company string`dynamodbav:"company"`
Score int`dynamodbav:"score"`
GSI1PK string`dynamodbav:"gsi1pk,omitempty"`
GSI1SK int`dynamodbav:"gsi1sk,omitempty"`}funcitemKey(id string, date int)map[string]types.AttributeValue {returnmap[string]types.AttributeValue{"id":&types.AttributeValueMemberS{Value: id},"date":&types.AttributeValueMemberN{Value: fmt.Sprint(date)},}}funcmain(){
ctx := context.Background()
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1"))if err !=nil{panic(err)}
client := dynamodb.NewFromConfig(cfg)
fmt.Printf("DynamoDB client ready: %T\n", client)}
All item examples use my-table with String partition key id and Number sort key date. The optional GSI1 index uses String gsi1pk and Number gsi1sk. Both key attributes are required for a single-item operation. The date values below are numeric YYYYMMDD examples, not Unix timestamps.
Define only the table and index key attributes in AttributeDefinitions. Other fields are schemaless. This creates an on-demand table and the index used later; it then waits for the table to become active. The two-minute wait is an application timeout, not a service guarantee.
DeleteTable removes the whole table, including its indexes and items. The waiter completes when the table no longer exists. Do not run this as part of ordinary application startup.
A Scan evaluates every item in the table or index. Process one page at a time to keep memory bounded. UnmarshalListOfMaps decodes a page into typed Go values, using the dynamodbav struct tags.
A filter reduces returned results after DynamoDB reads the items. It does not reduce the read capacity for those items. Here are equivalent manual and builder-based filters for items without deletedAt whose firstName contains John. Both return inputs for NewScanPaginator:
Use either input instead of the unfiltered ScanInput in scanItems. Continue even when a filtered page is empty. The paginator, not the number of returned items, determines when the scan is finished.
Dynobase's query workspace lets you inspect filters and save parameterized queries before putting them into application code.
The screenshot shows the Dynobase 3.0 query workspace with demo data. The Go SDK v2 examples above are the application-code reference.
Get item
Get an item by its complete primary key. ConsistentRead requests a strongly consistent base-table read. A missing item is a successful response with an empty Item, not a ResourceNotFoundException.
For a struct-based key, attributevalue.MarshalMap is equivalent to itemKey. Marshal only the key fields, not the full item:
funcmarshaledKey()(map[string]types.AttributeValue,error){return attributevalue.MarshalMap(struct{
ID string`dynamodbav:"id"`
Date int`dynamodbav:"date"`}{ID:"123", Date:20261023})}
Batch get item
BatchGetItem retrieves known keys, including keys from multiple tables. It accepts up to 100 items and a 16 MB response. Do not repeat a key in one batch. Results are unordered; missing items are omitted.
A successful response can still contain UnprocessedKeys. Retry only those keys with bounded exponential backoff and jitter. This helper also supports the batch-write example. The retry count and delay are example application choices.
PutItem replaces an existing item with the same complete key. Here MarshalMap encodes the struct, including the GSI attributes. Add ConditionExpression: aws.String("attribute_not_exists(id)") if overwriting that item should fail.
A batch accepts up to 25 puts or deletes and a 16 MB request. It does not support partial updates or per-item conditions, and the batch is not atomic. A put replaces the complete item. Retry UnprocessedItems even when the SDK returns no error.
For multiple tables, add another table-name entry to RequestItems and keep the operation total within the batch limit. If retries are exhausted, retain the outstanding requests for recovery. Do not log sensitive item contents in production.
Query for a set of items
A Query needs equality on the partition key and can add a sort-key condition. This example reads dates after a lower bound for one id. The expression builder creates aliases and typed values together; pass all three expression fields to the request.
Use a filter only for non-key conditions after the key selection. A Query filter cannot contain the queried partition or sort key. The Query vs. Scan comparison explains why a key-based access pattern usually reads less data.
Query an index
Set IndexName and use the index's key attributes. This GSI query finds company records by date, independently of the base-table id. It uses the same paginator as a table query. GSIs support eventual consistency only; do not set ConsistentRead to true.
A GSI query returns only projected attributes. This example's index projects all attributes. A local secondary index uses the table's partition key and a different sort key; specify its name in the same way, but construct the condition for that index's schema.
Simple transaction
Transactions make several writes succeed or fail together. The following cart example uses two items in my-table: an existing stock item with key stock#123 / 0 and a unique cart entry with key cart#456#product#123 / 20261023. Seed the stock item's Number count before calling it. The stock condition prevents decrementing below zero; the cart condition prevents reserving the same cart entry twice.
Supply a unique request token for each logical reservation and reuse it only for retries of the identical transaction. Transactions support up to 100 unique items and 4 MB in one account and Region, with at most one action on each item. They are unavailable on MRSC global tables. The token's idempotency window is 10 minutes after the request completes.
Read transaction
TransactGetItems reads specified items as a transaction, including across tables in the same account and Region. Responses correspond to the request order. It is not a transactional Query or Scan.
Query results are ordered by the queried sort key. Numbers sort numerically; strings sort by UTF-8 bytes. ScanIndexForward: false reverses the order. A Scan has no sort-order guarantee; a different sort attribute needs an index or application-side sorting.
Query and Scan evaluate up to 1 MB per service call before applying filters. Limit also counts evaluated items, not matching results. The paginators above keep requesting pages automatically; a limit on the input is a per-call limit, not a total limit across the paginator.
For a user-facing page, make one call and return its LastEvaluatedKey as a cursor. Pass the complete key map back as ExclusiveStartKey, including index-key components for index queries. Validate that a cursor belongs to the same authorized query before using it. See DynamoDB pagination for cursor semantics.
UpdateItem changes selected attributes and can create an item if its key is absent. Use ReturnValuesAllNew when you need the resulting attributes; without it, Attributes is normally empty. Alias attribute names to avoid reserved-word conflicts.
A conditional write checks the current item atomically with the update. This changes the name only if the item belongs to Apple and has no deletedAt. Build the update and condition together so their generated aliases do not collide.
The equivalent manual condition is attribute_not_exists(#deleted) AND #company = :company, with name aliases for deletedAt and company and a String value of Apple. A failed condition is an expected application outcome, not a reason to retry the same write indefinitely.
Use the standard library's errors.As to inspect wrapped SDK errors. AWS's error-handling guide explains why a direct type assertion can miss the underlying service exception.
funcdescribeError(err error)string{if err ==nil{return"success"}var condition *types.ConditionalCheckFailedException
var missing *types.ResourceNotFoundException
var throttled *types.ProvisionedThroughputExceededException
var canceled *types.TransactionCanceledException
switch{case errors.As(err,&condition):return"condition did not match"case errors.As(err,&missing):return"table or index was not found"case errors.As(err,&throttled):return"capacity exceeded after SDK retries"case errors.As(err,&canceled):return"transaction canceled: inspect cancellation reasons"default:return err.Error()}}
The SDK also models throttling errors such as types.ThrottlingException and types.RequestLimitExceeded. SDK retries do not replace handling a final failure, and successful batch responses require the separate unprocessed-item loops above.
Increment an item attribute
Use an atomic update instead of a read-modify-write cycle, which can lose concurrent changes. if_not_exists initializes a missing counter. This operation is atomic but not idempotent: retrying an uncertain application request can increment twice.
Delete by the complete key. Deleting an absent item succeeds. Add a condition when deletion must depend on the current state, or request ReturnValuesAllOld to inspect the removed item.
There is no table-wide SQL-style delete. Scan the keys and delete each item, or batch those keys in groups of up to 25 with the retry helper. This sequential version keeps the schema and error handling explicit. Stop other writers first if the table must end empty: a scan is not a point-in-time snapshot, and concurrent writes can survive or be deleted unexpectedly.
For a local DynamoDB instance, configure BaseEndpoint and dummy credentials. A local client still needs a Region for signing. These dummy values are only for local emulation. AWS's endpoint guide recommends the newer endpoint configuration instead of the legacy EndpointResolver.
Use the returned client with the same functions. Local emulation is useful for request shape and key-schema checks; it does not reproduce production throttling or every managed-service feature.
Frequently Asked Questions
How do I use DynamoDB with Golang?
Use AWS SDK for Go v2. Load configuration with config.LoadDefaultConfig, create a dynamodb.Client with NewFromConfig, and pass a context and typed input to each operation.
How do I query DynamoDB in Go?
Build a key condition with equality on the partition key and an optional sort-key condition, then use dynamodb.NewQueryPaginator to read all pages. A filter is applied after the key-matched items are read.
How do I unmarshal DynamoDB items into Go structs?
Use attributevalue.UnmarshalMap for one item and UnmarshalListOfMaps for a page. Export the struct fields and give them dynamodbav tags matching the attribute names.
Why does my Go Scan return only some items?
Each call evaluates up to 1 MB before filtering. Use NewScanPaginator, and keep going even when a filtered page contains no items.
How do I handle ConditionalCheckFailedException in Go?
Use errors.As with a pointer to types.ConditionalCheckFailedException. The exception means the write condition did not match; decide whether to report a conflict or re-read state.
Dynobase is a Professional GUI Client for DynamoDB