dynamobind

package
v0.4.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 9, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package dynamobind provides typed, reflection-free DynamoDB item binding on top of github.com/shibukawa/tinygodriver/nosql/dynamodb.

A struct declares its attributes once with dynamo tags, tinybind-gen emits the codec, and the call site never handles a map[string]dynamodb.AttributeValue:

type Reading struct {
	Sensor  string  `dynamo:"sensor,partitionkey"`
	At      int64   `dynamo:"at,sortkey"`
	Celsius float64 `dynamo:"celsius"`
}

ctx = dynamobind.WithClient(ctx, client, dynamobind.WithTablePrefix(""))
got, err := dynamobind.Load[Reading](ctx, "readings", want.ItemKey())

The client is not a parameter. It and the deployment table prefix are facts of one process, installed once with WithClient, so no call site and no generated signature carries them; see context.go.

Dispatch is by type constraint rather than by a registry, so a type without generated code fails to compile instead of failing at run time on a missing registration. Nothing here reflects on application fields.

What this package does not do

It adds no retry loop: the driver already retries with backoff, and a second loop would multiply the delivery count silently. It hides no page boundary: Query and Scan iterate, but QueryPage stays public and returns LastEvaluatedKey. It swallows no error: every driver sentinel survives errors.Is and *dynamodb.Error survives errors.As through every helper here.

Index

Constants

View Source
const (
	MaxBatchWrite = 25
	MaxBatchGet   = 100
)

DynamoDB's per-request batch limits. They are fixed by the service, so the chunking that respects them is mechanical and belongs here rather than in generated code. The size limits, 16 MB per request and 400 KB per item, are properties of the data rather than of the type, so nothing at generation time could bound them; an oversized request surfaces as dynamodb.ErrRequestTooLarge.

Variables

View Source
var ErrNoClient = errors.New("dynamobind: no DynamoDB client in context")

ErrNoClient reports that a Context does not carry a DynamoDB client, or that a zero Handle was passed to an entry taking one. It is returned rather than panicking, so every entry stays an ordinary error-returning function.

Functions

func ClientFromContext added in v0.2.10

func ClientFromContext(ctx context.Context) (*dynamodb.Client, error)

ClientFromContext returns the client installed by WithClient.

It is the escape hatch for reaching the driver directly, for an operation this package does not wrap. Everything this package does wrap resolves through TableFromContext instead, so the name mapping is applied.

func Load

func Load[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, table string, key dynamodb.Key, opts ...dynamodb.GetOption) (T, error)

Load reads one item by key and decodes it into T.

A key that matches nothing keeps the driver's dynamodb.ErrItemNotFound rather than returning a zero value, so a miss cannot be mistaken for an empty item.

func LoadAll

func LoadAll[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, table string, keys []dynamodb.Key, opts ...dynamodb.BatchOption) ([]T, []dynamodb.Key, error)

LoadAll reads every key, splitting the input into requests of at most MaxBatchGet keys.

DynamoDB does not promise to return batch items in request order, and a key that matches nothing is simply absent rather than an error, so len(items) can be smaller than len(keys) with no error and no unprocessed key. Keys DynamoDB declined to read come back in unprocessed, unretried.

func LoadAllOn added in v0.3.7

func LoadAllOn[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, h Handle, table string, keys []dynamodb.Key, opts ...dynamodb.BatchOption) ([]T, []dynamodb.Key, error)

LoadAllOn is LoadAll taking its Handle as an argument.

func LoadOn added in v0.3.7

func LoadOn[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, h Handle, table string, key dynamodb.Key, opts ...dynamodb.GetOption) (T, error)

LoadOn is Load taking its Handle as an argument.

func Query

func Query[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, table, keyCond string, opts ...dynamodb.QueryOption) iter.Seq2[T, error]

Query iterates every item of a query, requesting pages as the range advances.

One range can issue many requests. The iterator reports no page boundary, no Count, no ScannedCount, and no final LastEvaluatedKey, so a query that scans far more than it returns looks the same as one that does not, and an interrupted run cannot be resumed. Use QueryPage when any of that matters.

Iteration stops at the first error, which is yielded once with the zero value of T. A break stops it without issuing a further request.

func QueryOn added in v0.3.7

func QueryOn[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, h Handle, table, keyCond string, opts ...dynamodb.QueryOption) iter.Seq2[T, error]

QueryOn is Query taking its Handle as an argument. The Handle is resolved once for the whole range rather than once per page.

func Remove

func Remove[T Keyer](ctx context.Context, table string, v T, opts ...dynamodb.WriteOption) error

Remove deletes the item identified by v's key. Only the key of v is read.

func RemoveOn added in v0.3.7

func RemoveOn[T Keyer](ctx context.Context, h Handle, table string, v T, opts ...dynamodb.WriteOption) error

RemoveOn is Remove taking its Handle as an argument.

func RemoveReturning

func RemoveReturning[T Keyer, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, table string, v T, opts ...dynamodb.WriteOption) (T, bool, error)

RemoveReturning is Remove, and also decodes the item it deleted.

The bool is false when the key held no item, which is not an error.

func RemoveReturningOn added in v0.3.7

func RemoveReturningOn[T Keyer, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, h Handle, table string, v T, opts ...dynamodb.WriteOption) (T, bool, error)

RemoveReturningOn is RemoveReturning taking its Handle as an argument.

func Scan

func Scan[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, table string, opts ...dynamodb.ScanOption) iter.Seq2[T, error]

Scan iterates every item of a table or index scan.

An unfiltered scan walks the whole table, one page per request. Everything said about Query's hidden request count applies here and costs more.

func ScanOn added in v0.3.7

func ScanOn[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, h Handle, table string, opts ...dynamodb.ScanOption) iter.Seq2[T, error]

ScanOn is Scan taking its Handle as an argument. The Handle is resolved once for the whole range rather than once per page.

func Store

func Store[T ItemEncoder](ctx context.Context, table string, v T, opts ...dynamodb.WriteOption) error

Store writes v as a whole item, replacing any item with the same key.

It is PutItem, not a partial update: every attribute of the stored item comes from v. Use Update for a partial change.

func StoreAll

func StoreAll[T ItemEncoder](ctx context.Context, table string, vs []T) ([]T, error)

StoreAll writes every value, splitting the input into requests of at most MaxBatchWrite items.

What DynamoDB declined comes back in unprocessed, unretried: the driver already retries the transport, and whether a partial success is worth a second attempt is the caller's decision, not this package's. Passing unprocessed back to StoreAll is the retry, and it belongs in caller code where a backoff can live.

func StoreAllOn added in v0.3.7

func StoreAllOn[T ItemEncoder](ctx context.Context, h Handle, table string, vs []T) ([]T, error)

StoreAllOn is StoreAll taking its Handle as an argument.

func StoreOn added in v0.3.7

func StoreOn[T ItemEncoder](ctx context.Context, h Handle, table string, v T, opts ...dynamodb.WriteOption) error

StoreOn is Store taking its Handle as an argument.

func StoreReturning

func StoreReturning[T ItemEncoder, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, table string, v T, opts ...dynamodb.WriteOption) (T, bool, error)

StoreReturning is Store, and also decodes the item it replaced.

The bool is false when the key held no item, which is not an error. It asks the driver for ALL_OLD, so it costs a write capacity unit more than Store on a table that charges for it.

func StoreReturningOn added in v0.3.7

func StoreReturningOn[T ItemEncoder, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, h Handle, table string, v T, opts ...dynamodb.WriteOption) (T, bool, error)

StoreReturningOn is StoreReturning taking its Handle as an argument.

func TableFromContext added in v0.2.10

func TableFromContext(ctx context.Context, table string) (*dynamodb.Client, string, error)

TableFromContext resolves a declared table name into the client and the name to send. Every Context-resolving entry of this package calls it.

func TypeError

func TypeError(attribute, expected string, got dynamodb.AttributeValue) error

TypeError reports an attribute whose stored kind is not the one the field needs. Generated decoders call it.

func Update

func Update[T Keyer](ctx context.Context, table string, v T, update string, opts ...dynamodb.WriteOption) error

Update applies a DynamoDB update expression to the item identified by v's key.

The expression is passed to the driver verbatim; nothing here generates or validates it. Only the key is typed, which is the part a struct tag can actually supply. Attribute values in the expression come from dynamodb.WithExpressionValues as usual:

err := dynamobind.Update(ctx, "readings", key, "SET celsius = :c",
	dynamodb.WithExpressionValues(map[string]dynamodb.AttributeValue{":c": dynamodb.N(21.5)}))

func UpdateOn added in v0.3.7

func UpdateOn[T Keyer](ctx context.Context, h Handle, table string, v T, update string, opts ...dynamodb.WriteOption) error

UpdateOn is Update taking its Handle as an argument.

func ValueError

func ValueError(attribute, message string, cause error) error

ValueError reports an attribute whose kind is right but whose value cannot be represented by the field, such as a number too large for the Go type. Generated decoders call it.

func WithClient added in v0.2.10

func WithClient(ctx context.Context, c *dynamodb.Client, options ...ClientOption) context.Context

WithClient returns a child Context carrying a DynamoDB client and, with WithTableNames, how its tables are named. Framework middleware installs it once, and every entry of this package resolves it.

func WithHandle added in v0.3.7

func WithHandle(ctx context.Context, h Handle) context.Context

WithHandle returns a child Context carrying an already-built Handle.

It is WithClient for a caller that holds a Handle: a framework resolving one out of its own Context value, or a setup path that builds the Handle once and installs it in several Contexts.

Types

type ClientOption added in v0.2.10

type ClientOption func(*Handle)

ClientOption configures what a stored client is used with.

func WithTableNames added in v0.2.10

func WithTableNames(resolve TableResolver) ClientOption

WithTableNames records how declared table names map onto this deployment's. Without it the declared name is sent unchanged, which is what a deployment whose tables are named as declared wants. A nil resolver is ignored, so a mistaken nil behaves as no resolver rather than panicking on the first call.

type Error

type Error struct {
	// Attribute is the attribute that failed, or "" for a whole-item failure.
	Attribute string
	// Expected and Got name attribute kinds, such as "S" or "N".
	Expected string
	Got      string
	Message  string
	// contains filtered or unexported fields
}

Error describes an item mapping failure. Attribute names the attribute that failed, which is the DynamoDB name rather than the Go field name, because that is the name the stored data uses.

func AsError

func AsError(err error) (*Error, bool)

AsError finds a dynamobind Error in a chain without errors.As, which needs reflection. Whether reflect is linked at all is the driver's business, not this package's.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

type Handle added in v0.3.7

type Handle struct {
	// contains filtered or unexported fields
}

Handle is a DynamoDB client together with the table naming of one deployment.

It is what WithClient stores in a Context, and it is what the entries suffixed On take directly. The two forms are the same value reached two ways: a Context keeps it off every call site, and a parameter keeps it out of every lookup. Which one a program uses is a call-site preference, not a behaviour difference.

The zero Handle carries no client, so an entry given one returns ErrNoClient exactly as a Context carrying none does.

Fields are unexported so a field added later is not a breaking change; build one with NewHandle.

func HandleFromContext added in v0.3.7

func HandleFromContext(ctx context.Context) (Handle, error)

HandleFromContext returns the Handle installed by WithClient or WithHandle.

It is the one lookup a caller needs: a framework reading it once in middleware has the client and the table naming in hand, and can then call the entries suffixed On with no further Context lookup on the operation path.

func NewHandle added in v0.3.7

func NewHandle(c *dynamodb.Client, options ...ClientOption) Handle

NewHandle binds a client to the table naming of one deployment, for the entries suffixed On.

It takes the same ClientOption list as WithClient, so a program moving between the two forms rewrites the call and not the configuration:

h := dynamobind.NewHandle(client, dynamobind.WithTableNames(names))
r, err := dynamobind.LoadOn[Reading](ctx, h, "readings", key)

func (Handle) Client added in v0.3.7

func (h Handle) Client() *dynamodb.Client

Client returns the driver client this Handle carries, or nil for the zero Handle. It is the escape hatch for reaching the driver directly, and it applies no table naming; Table is what applies that.

func (Handle) Table added in v0.3.7

func (h Handle) Table(ctx context.Context, table string) (*dynamodb.Client, string, error)

Table resolves a declared table name into the client and the name to send. Every entry suffixed On calls it, and so does TableFromContext once it has read the Handle out of a Context.

type ItemDecoder

type ItemDecoder interface {
	DecodeItem(item dynamodb.Item) error
}

ItemDecoder fills a value from a DynamoDB item. Generated code implements it on the pointer receiver.

type ItemEncoder

type ItemEncoder interface {
	EncodeItem() dynamodb.Item
}

ItemEncoder converts a value into a DynamoDB item. Generated code implements it on the value receiver.

type Keyer

type Keyer interface {
	ItemKey() dynamodb.Key
}

Keyer reports the primary key of a value. Generated code implements it when the type carries a partitionkey tag.

type Page

type Page[T any] struct {
	Items            []T
	LastEvaluatedKey dynamodb.Key
	Count            int
	ScannedCount     int
}

Page is one page of decoded items.

LastEvaluatedKey is the continuation and is the authority on whether more pages follow: a non-nil key means more, whatever Count says, because a filter can empty a page that still has a successor.

func QueryPage

func QueryPage[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, table, keyCond string, opts ...dynamodb.QueryOption) (Page[T], error)

QueryPage runs one Query and decodes its page.

This is the form that keeps the request count visible: one call is one request. Query iterates instead, at the cost of hiding how many requests that takes.

func QueryPageOn added in v0.3.7

func QueryPageOn[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, h Handle, table, keyCond string, opts ...dynamodb.QueryOption) (Page[T], error)

QueryPageOn is QueryPage taking its Handle as an argument.

func ScanPage

func ScanPage[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, table string, opts ...dynamodb.ScanOption) (Page[T], error)

ScanPage runs one Scan and decodes its page.

func ScanPageOn added in v0.3.7

func ScanPageOn[T any, PT interface {
	*T
	ItemDecoder
}](ctx context.Context, h Handle, table string, opts ...dynamodb.ScanOption) (Page[T], error)

ScanPageOn is ScanPage taking its Handle as an argument.

func (Page[T]) HasMore

func (p Page[T]) HasMore() bool

HasMore reports whether another page follows this one.

type TableResolver added in v0.2.10

type TableResolver func(ctx context.Context, declared string) string

TableResolver maps the name a declaration or a call site writes onto the name this deployment uses.

It takes the Context so the mapping can depend on the request as well as on the process: a per-tenant table, or a name read from configuration bound to the Context, is the same one function. Nothing here composes the name, so a prefix, a suffix, a lookup table and a wholly unrelated name are equally expressible:

dynamobind.WithTableNames(func(ctx context.Context, declared string) string {
	return "staging-" + declared
})

dynamobind.WithTableNames(func(ctx context.Context, declared string) string {
	return config.Tables[declared] // whatever the IaC named it
})

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL