# guregu/dynamo for Go DynamoDB: API, Encoding, and Limits

> guregu/dynamo is a Go client for Amazon DynamoDB that builds requests through table-scoped chains, handles expressions, and maps values to Go structs. It suits applications that want a concise data layer, but its custom licence and Go 1.25.0 requirement need review before adoption.

**guregu/dynamo** — expressive DynamoDB library for Go

- Repository: https://github.com/guregu/dynamo
- Stars: 1,387 · Forks: 181
- Language: Go
- License: NOASSERTION
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/guregu-dynamo

## Why guregu/dynamo Wraps AWS DynamoDB

guregu/dynamo targets Go applications that read and write Amazon DynamoDB through the official AWS SDK v2. The README describes it as an expressive DynamoDB client, while the module path is `github.com/guregu/dynamo/v2`. It is intended for teams that have selected DynamoDB but want a smaller application API than the AWS service client exposes.

The library is not independent of DynamoDB. Its documentation refers to DynamoDB expression syntax and AWS SDK v2, and `go.mod` includes the AWS DynamoDB service package and the `feature/dynamodb/attributevalue` package. The wrapper adds Go-specific request construction and value mapping, but AWS still determines service semantics, billing, limits, consistency, and data-model constraints.

Its intended users are Go developers working with hash and range keys, secondary indexes, conditions, batches, transactions, and item projections. It is less suitable for a project that wants to move between storage backends without rewriting application-specific DynamoDB expressions. The API reduces repetitive request assembly, but the data model remains DynamoDB-specific.

## How Table Operations Build Requests

The visible data flow begins with `dynamo.New(cfg)`, followed by `db.Table("Widgets")`. The returned table is the entry point for item operations. `Put` accepts a Go value. `Get` receives a partition key, and a range key can be added through `Range`. Results are decoded explicitly: `One` targets one destination, while `All` targets a collection.

This design separates operation description from execution. Application code first describes what it wants, then supplies a context and destination or error return path. That makes call sites compact, but it also means developers need to understand which methods terminate a chain and which values must be supplied at each stage. The README example uses `Put`, `Get`, `Scan`, `Filter`, and `Run`, but it does not provide a complete reference for every terminal method.

The repository layout supports a wider API than the README excerpt describes. It includes files for queries, scans, puts, deletes, updates, batch operations, transactions, table creation, table description, conditional checks, server-side encryption, time-to-live handling, retries, and table updates. The file names establish where functionality is implemented, but method signatures and detailed error behaviour should be confirmed in the Go package documentation before an application depends on them.

## DynamoDB Expressions and Access Costs

guregu/dynamo lets callers write DynamoDB filter and condition expressions through helper methods. Question marks represent attribute values. Dollar signs represent attribute names or list indexes. Attribute names can also be written directly when they are not reserved words, or escaped with single quotes. The README specifically recommends escaping names because DynamoDB has a large reserved-word list.

This placeholder design avoids directly concatenating application values into an expression string. It does not, however, determine whether an operation is efficient. A `Scan` can still read items across the table, and a filter only selects from the items returned by that scan. The documentation shows filtered scans but does not claim that filtering reduces DynamoDB read costs.

Query design still depends on the table's keys and indexes. The `Get` example demonstrates partition and range keys. Release `v2.6.0` is labelled "Composite GSI support", but the README excerpt does not explain how a composite global secondary index is declared or queried. A team using that feature should check the release documentation and package API rather than infer its behaviour from the release label.

The direct AWS SDK approach exposes service operations and request objects. guregu/dynamo offers a different application-facing style, with chaining, expression placeholders, and automatic Go value mapping. That convenience costs a dependency on the wrapper's conventions, which applications must learn and version alongside their own code.

## Struct Tags, Sets, and Encoding Rules

Struct mapping is a central feature. The `dynamo` tag uses a form similar to the standard `encoding/json` tag. A field can rename its DynamoDB attribute, omit zero values, encode as a set, or be excluded. The documented exclusion marker is `dynamo:"-"`. Lowercase fields are ignored unless they belong to an embedded type that exposes uppercase fields.

Omission rules need schema-level attention. `omitempty` removes zero values such as empty strings, zero numbers, nil pointers, and empty structs. Dynamo also automatically omits several empty values, including empty strings, empty sets, empty structs, nil pointers and interfaces, empty binary values, and text-marshaled values that produce no bytes. The `allowempty` flag overrides automatic omission where the value can be stored. The README gives a boundary: an empty set remains omitted because DynamoDB cannot store it.

Slices are DynamoDB lists by default. The `dynamo:",set"` option encodes supported slices as sets. Supported map forms include `[]T`, `map[T]struct{}`, and `map[T]bool`, where `T` marshals to a DynamoDB string, number, or binary value. Set order is undefined, so application logic must not depend on the sequence in which values appear.

The encoder also supports `dynamo.Marshaler`, `dynamo.Unmarshaler`, AWS `dynamodbattribute.Marshaler` and `Unmarshaler`, and Go text marshaling interfaces. This permits custom representations and supports types such as `time.Time`. A field naming collision is resolved according to Go field priority, as documented in the README. Schema changes should therefore be reviewed as code changes, not treated as automatic compatibility changes.

## Installing v2 and Running the First Use

The README identifies the module path as `github.com/guregu/dynamo/v2`, and the repository's `go.mod` file declares the module and Go version. The project says it is versioned with Go modules.

The first real use starts by importing the AWS configuration package and dynamo, loading a configuration for `us-east-1`, creating the client, and selecting `Widgets`:

```go
import (
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/guregu/dynamo/v2"
)

cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-east-1"))
if err != nil {
	log.Fatalf("unable to load SDK config, %v", err)
}

db := dynamo.New(cfg)
table := db.Table("Widgets")
```

After that setup, the README demonstrates writing and reading a `widget`. The example uses a context and checks errors in production code:

```go
w := widget{UserID: 613, Time: time.Now(), Msg: "hello"}
err = table.Put(w).Run(ctx)

var result widget
err = table.Get("UserID", w.UserID).
	Range("Time", dynamo.Equal, w.Time).
	One(ctx, &result)
```

The table can then be read with `Scan`. The first call returns all matching items into a slice, while the second applies a value placeholder filter:

```go
var results []widget
err = table.Scan().All(ctx, &results)

var filtered []widget
err = table.Scan().Filter("'Count' > ?", 10).All(ctx, &filtered)
```

A first-use review should verify that the application's context is initialized, that the table and key names match the deployed schema, and that AWS credentials are available to the SDK configuration. The README does not document a special dynamo-specific credential setup.

## Limitations, Alternatives, and Adoption Boundaries

The clearest limitation is that the library remains tied to DynamoDB and AWS SDK v2. It does not hide the service's access model, capacity behaviour, consistency choices, or index requirements. Filtering a `Scan` can make code readable while still producing an expensive table-wide operation. A project needing portable persistence should choose an abstraction that defines its own storage interface instead of making DynamoDB expressions part of its domain layer.

The alternative is direct use of the AWS SDK v2 DynamoDB client. That approach exposes more of the service request model and gives engineers direct control over requests, but it adds repetitive construction of expressions, attributes, and result decoding. guregu/dynamo instead supplies chaining and Go-native mapping. The choice is between a focused wrapper and direct control, not between DynamoDB and a different database.

Another limitation is documentation depth in the excerpt. It does not document rollback, complete error categories, context cancellation behaviour, pagination controls, or the precedence between every supported custom encoder. Those are not reasons to reject the project, but they are reasons to inspect the package documentation and tests for the exact features an application will use.

The repository is not archived, and the last push was on 2026-04-14. Release `v2.6.0` was published on 2026-04-14, after `v2.5.0` on 2025-12-17. This indicates recent project work, but it does not establish compatibility guarantees for every future upgrade. The adoption decision should therefore account for API review, module upgrades, and schema tests rather than relying on release frequency alone.

## Conclusion

Application developers using AWS SDK v2 can adopt guregu/dynamo when chained table operations and Go-native struct encoding are useful. Developers using AWS SDK v1 should remain on the documented v1 release for now. Before adoption, inspect LICENSE for its exact permissions and restrictions, confirm the environment can use Go 1.25.0, and verify that struct tags and omission rules match the stored data model.

## FAQ

### Does guregu/dynamo support Amazon DynamoDB with AWS SDK v2?

Yes. The README states that dynamo v2 integrates with the official AWS SDK v2, and `go.mod` requires AWS SDK v2 service and configuration packages.

### Which Go version does guregu/dynamo v2 require?

The `go.mod` file declares `go 1.25.0`. Consumers therefore need a compatible Go toolchain and build environment.

### How are Go slices stored by guregu/dynamo?

Slices are encoded as DynamoDB lists by default. The `dynamo:",set"` option encodes supported slices as sets, but set order is undefined.

### Does the guregu/dynamo README provide an installation command?

No. The README identifies the module path as `github.com/guregu/dynamo/v2` but does not include an installation command.

### What licence applies to guregu/dynamo?

The repository is classified as using a custom licence that GitHub cannot categorise automatically. The exact terms must be read from the `LICENSE` file.

## Sources

- [guregu/dynamo on GitHub](https://github.com/guregu/dynamo)
- [Issues](https://github.com/guregu/dynamo/issues)
- [README](https://github.com/guregu/dynamo/blob/master/README.md)
- [Releases](https://github.com/guregu/dynamo/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/guregu-dynamo
