{"id":13526565,"url":"https://github.com/guregu/dynamo","last_synced_at":"2025-05-13T19:18:07.676Z","repository":{"id":28024788,"uuid":"31519970","full_name":"guregu/dynamo","owner":"guregu","description":"expressive DynamoDB library for Go","archived":false,"fork":false,"pushed_at":"2024-12-17T20:06:02.000Z","size":569,"stargazers_count":1337,"open_issues_count":34,"forks_count":181,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-28T10:53:09.965Z","etag":null,"topics":["dynamodb","go"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/guregu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"guregu"}},"created_at":"2015-03-02T02:29:49.000Z","updated_at":"2025-04-18T07:36:01.000Z","dependencies_parsed_at":"2024-01-13T15:37:31.691Z","dependency_job_id":"903eb84b-3fef-45b6-83bb-c9b5d475319c","html_url":"https://github.com/guregu/dynamo","commit_stats":{"total_commits":315,"total_committers":38,"mean_commits":8.289473684210526,"dds":"0.18095238095238098","last_synced_commit":"929601d1c392717f59a8da6aa280405ee4629339"},"previous_names":[],"tags_count":56,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guregu%2Fdynamo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guregu%2Fdynamo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guregu%2Fdynamo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guregu%2Fdynamo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/guregu","download_url":"https://codeload.github.com/guregu/dynamo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254010830,"owners_count":21999004,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dynamodb","go"],"created_at":"2024-08-01T06:01:31.603Z","updated_at":"2025-05-13T19:18:07.652Z","avatar_url":"https://github.com/guregu.png","language":"Go","funding_links":["https://github.com/sponsors/guregu"],"categories":["Go","Repositories"],"sub_categories":[],"readme":"## dynamo [![GoDoc](https://godoc.org/github.com/guregu/dynamo/v2?status.svg)](https://godoc.org/github.com/guregu/dynamo/v2)\n`import \"github.com/guregu/dynamo/v2\"`\n\ndynamo is an expressive [DynamoDB](https://aws.amazon.com/dynamodb/) client for Go, with an easy but powerful API. dynamo integrates with the official [AWS SDK v2](https://github.com/aws/aws-sdk-go-v2/).\n\nThis library is stable and versioned with Go modules.\n\n\u003e [!TIP]\n\u003e dynamo v2 is finally released! See [**v2 Migration**](#migrating-from-v1) for tips on migrating from dynamo v1.\n\u003e \n\u003e For dynamo v1, which uses [aws-sdk-go v1](https://github.com/aws/aws-sdk-go/), see: [**dynamo v1 documentation**](https://pkg.go.dev/github.com/guregu/dynamo).\n\n### Example\n\n```go\npackage dynamo\n\nimport (\n\t\"time\"\n\t\"context\"\n\t\"log\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/config\"\n\t\"github.com/guregu/dynamo/v2\"\n)\n\n// Use struct tags much like the standard JSON library,\n// you can embed anonymous structs too!\ntype widget struct {\n\tUserID int       // Hash key, a.k.a. partition key\n\tTime   time.Time // Range key, a.k.a. sort key\n\n\tMsg       string              `dynamo:\"Message\"`    // Change name in the database\n\tCount     int                 `dynamo:\",omitempty\"` // Omits if zero value\n\tChildren  []widget            // List of maps\n\tFriends   []string            `dynamo:\",set\"` // Sets\n\tSet       map[string]struct{} `dynamo:\",set\"` // Map sets, too!\n\tSecretKey string              `dynamo:\"-\"`    // Ignored\n}\n\n\nfunc main() {\n\tcfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(\"us-east-1\"))\n\tif err != nil {\n\t\tlog.Fatalf(\"unable to load SDK config, %v\", err)\n\t}\n\tdb := dynamo.New(cfg)\n\ttable := db.Table(\"Widgets\")\n\n\t// put item\n\tw := widget{UserID: 613, Time: time.Now(), Msg: \"hello\"}\n\terr = table.Put(w).Run(ctx)\n\n\t// get the same item\n\tvar result widget\n\terr = table.Get(\"UserID\", w.UserID).\n\t\tRange(\"Time\", dynamo.Equal, w.Time).\n\t\tOne(ctx, \u0026result)\n\n\t// get all items\n\tvar results []widget\n\terr = table.Scan().All(ctx, \u0026results)\n\n\t// use placeholders in filter expressions (see Expressions section below)\n\tvar filtered []widget\n\terr = table.Scan().Filter(\"'Count' \u003e ?\", 10).All(ctx, \u0026filtered)\n}\n```\n\n### Expressions\n\ndynamo will help you write expressions used to filter results in queries and scans, and add conditions to puts and deletes. \n\nAttribute names may be written as is if it is not a reserved word, or be escaped with single quotes (`''`). You may also use dollar signs (`$`) as placeholders for attribute names and list indexes. DynamoDB has [very large amount of reserved words](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html) so it may be a good idea to just escape everything.\n\nQuestion marks (`?`) are used as placeholders for attribute values. DynamoDB doesn't have value literals, so you need to substitute everything.\n\nPlease see the [DynamoDB reference on expressions](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.SpecifyingConditions.html#ConditionExpressionReference) for more information. The [Comparison Operator and Function Reference](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) is also handy.\n\n```go\n// Using single quotes to escape a reserved word, and a question mark as a value placeholder.\n// Finds all items whose date is greater than or equal to lastUpdate.\ntable.Scan().Filter(\"'Date' \u003e= ?\", lastUpdate).All(ctx, \u0026results)\n\n// Using dollar signs as a placeholder for attribute names.\n// Deletes the item with an ID of 42 if its score is at or below the cutoff, and its name starts with G.\ntable.Delete(\"ID\", 42).If(\"Score \u003c= ? AND begins_with($, ?)\", cutoff, \"Name\", \"G\").Run(ctx)\n\n// Put a new item, only if it doesn't already exist.\ntable.Put(item{ID: 42}).If(\"attribute_not_exists(ID)\").Run(ctx)\n```\n\n### Encoding support\n\ndynamo automatically handles the following interfaces:\n\n* [`dynamo.Marshaler`](https://godoc.org/github.com/guregu/dynamo#Marshaler) and [`dynamo.Unmarshaler`](https://godoc.org/github.com/guregu/dynamo#Unmarshaler)\n* [`dynamodbattribute.Marshaler`](https://godoc.org/github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute#Marshaler) and [`dynamodbattribute.Unmarshaler`](https://godoc.org/github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute#Unmarshaler)\n* [`encoding.TextMarshaler`](https://godoc.org/encoding#TextMarshaler) and [`encoding.TextUnmarshaler`](https://godoc.org/encoding#TextUnmarshaler)\n\nThis allows you to define custom encodings and provides built-in support for types such as `time.Time`.\n\n### Struct tags and fields\n\ndynamo handles struct tags similarly to the standard library `encoding/json` package. It uses `dynamo` for the struct tag's name, taking the form of: `dynamo:\"attributeName,option1,option2,etc\"`. You can omit the attribute name to use the default: `dynamo:\",option1,etc\"`.\n\n#### Renaming\n\nBy default, dynamo will use the name of your fields as the name of the DynamoDB attribute it corresponds do. You can specify a different name with the `dynamo` struct tag like so: `dynamo:\"other_name_goes_here\"`. If two fields have the same name, dynamo will prioritize the higher-level field.\n\n#### Omission\n\nIf you set a field's name to `\"-\"` (as in `dynamo:\"-\"`) that field will be ignored. It will be omitted when marshaling and ignored when unmarshaling. Also, fields that start with a lowercase letter will be ignored. However, embedding a struct whose type has a lowercase letter but contains uppercase fields is OK.\n\n#### Sets\n\nBy default, slices will be marshaled as DynamoDB lists. To marshal a field to sets instead, use the `dynamo:\",set\"` option. Empty sets will be automatically omitted.\n\nYou can use maps as sets too. The following types are supported:\n\n- `[]T`\n- `map[T]struct{}`\n- `map[T]bool`\n\nwhere `T` represents any type that marshals into a DynamoDB string, number, or binary value. \n\nNote that the order of objects within a set is undefined.\n\n#### Omitting empty values (omitempty)\n\nUsing the **omitempty** option (as in `dynamo:\",omitempty\"`) will omit the field if it has a zero (ex. an empty string, 0, nil pointer) value. Structs are supported. \n\nIt also supports the `isZeroer` interface below:\n\n```go\ntype isZeroer interface {\n\tIsZero() bool\n}\n```\n\nIf `IsZero()` returns true, the field will be omitted. This gives us built-in support for `time.Time`. \n\nYou can also use the `dynamo:\",omitemptyelem\"` option to omit empty values inside of slices.\n\n#### Automatic omission\n\nSome values will be automatically omitted.\n\n- Empty strings\n- Empty sets\n- Empty structs\n- Nil pointers and interfaces\n- Types that implement `encoding.TextMarshaler` and whose `MarshalText` method returns 0-length or nil slice.\n- Zero-length binary (byte slices)\n\nTo override this behavior, use the `dynamo:\",allowempty\"` flag. Not all empty types can be stored by DynamoDB. For example, empty sets will still be omitted.\n\nTo override auto-omit behavior for children of a map, for example `map[string]string`, use the `dynamo:\",allowemptyelem\"` option.\n\n#### Using the NULL type\n\nDynamoDB has a special NULL type to represent null values. In general, this library avoids marshaling things as NULL and prefers to omit those values instead. If you want empty/nil values to marshal to NULL, use the `dynamo:\",null\"` option.\n\n#### Unix time\n\nBy default, `time.Time` will marshal to a string because it implements `encoding.TextMarshaler`.\n\nIf you want `time.Time` to marshal as a Unix time value (number of seconds since the Unix epoch), you can use the `dynamo:\",unixtime\"` option. This is useful for TTL fields, which must be Unix time.\n\n### Creating tables\n\nYou can use struct tags to specify hash keys, range keys, and indexes when creating a table.\n\nFor example:\n\n```go\ntype UserAction struct {\n\tUserID string    `dynamo:\"ID,hash\" index:\"Seq-ID-index,range\"`\n\tTime   time.Time `dynamo:\",range\"`\n\tSeq    int64     `localIndex:\"ID-Seq-index,range\" index:\"Seq-ID-index,hash\"`\n\tUUID   string    `index:\"UUID-index,hash\"`\n}\n```\n\nThis creates a table with the primary hash key ID and range key Time. It creates two global secondary indices called UUID-index and Seq-ID-index, and a local secondary index called ID-Seq-index.\n\n### Retrying\n\nAs of v2, dynamo relies on the AWS SDK for retrying. See: [**Retries and Timeouts documentation**](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/retries-timeouts/) for information about how to configure its behavior.\n\nBy default, canceled transactions (i.e. errors from conflicting transactions) will not be retried. To get automatic retrying behavior like in v1, use [`dynamo.RetryTxConflicts`](https://godoc.org/github.com/guregu/dynamo/v2#RetryTxConflicts).\n\n```go\nimport (\n\t\"context\"\n\t\"log\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/aws/retry\"\n\t\"github.com/aws/aws-sdk-go-v2/config\"\n\t\"github.com/guregu/dynamo/v2\"\n)\n\nfunc main() {\n\tcfg, err := config.LoadDefaultConfig(context.Background(), config.WithRetryer(func() aws.Retryer {\n\t\treturn retry.NewStandard(dynamo.RetryTxConflicts)\n\t}))\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdb := dynamo.New(cfg)\n\t// use db\n}\n```\n\n### Compatibility with the official AWS library\n\ndynamo has been in development before the official AWS libraries were stable. We use a different encoder and decoder than the [dynamodbattribute](https://pkg.go.dev/github.com/jviney/aws-sdk-go-v2/service/dynamodb/dynamodbattribute) package. dynamo uses the `dynamo` struct tag instead of the `dynamodbav` struct tag, and we also prefer to automatically omit invalid values such as empty strings, whereas the dynamodbattribute package substitutes null values for them. Items that satisfy the [`dynamodbattribute.(Un)marshaler`](https://pkg.go.dev/github.com/jviney/aws-sdk-go-v2/service/dynamodb/dynamodbattribute#Marshaler) interfaces are compatibile with both libraries.\n\nIn order to use dynamodbattribute's encoding facilities, you must wrap objects passed to dynamo with [`dynamo.AWSEncoding`](https://godoc.org/github.com/guregu/dynamo/v2#AWSEncoding). Here is a quick example:\n\n```go\n// Notice the use of the dynamodbav struct tag\ntype book struct {\n\tID    int    `dynamodbav:\"id\"`\n\tTitle string `dynamodbav:\"title\"`\n}\n// Putting an item\nerr := db.Table(\"Books\").Put(dynamo.AWSEncoding(book{\n\tID:    42,\n\tTitle: \"Principia Discordia\",\n})).Run(ctx)\n// When getting an item you MUST pass a pointer to AWSEncoding!\nvar someBook book\nerr := db.Table(\"Books\").Get(\"ID\", 555).One(ctx, dynamo.AWSEncoding(\u0026someBook))\n```\n\n### Migrating from v1\n\nThe API hasn't changed much from v1 to v2. Here are some migration tips:\n\n- All request methods now take a [context](https://go.dev/blog/context) as their first argument.\n- Retrying relies on the AWS SDK configuration, see: [Retrying](#retrying).\n  - Transactions won't retry TransactionCanceled responses by default anymore, make sure you configure that if you need it.\n- Arguments that took `int64` (such as in `Query.Limit`) now take `int` instead.\n- [Compatibility with the official AWS library](#compatibility-with-the-official-aws-library) uses v2 interfaces instead of v1.\n- `KMSMasterKeyArn` renamed to `KMSMasterKeyARN`.\n\n### Integration tests\n\nBy default, tests are run in offline mode. In order to run the integration tests, some environment variables need to be set.\n\nTo run the tests against [DynamoDB Local](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html):\n\n```bash\n# Use Docker to run DynamoDB local on port 8880\ndocker compose -f '.github/docker-compose.yml' up -d\n\n# Run the tests with a fresh table\n# The tables will be created automatically\n# The '%' in the table name will be replaced the current timestamp\nDYNAMO_TEST_ENDPOINT='http://localhost:8880' \\\n\tDYNAMO_TEST_REGION='local' \\\n\tDYNAMO_TEST_TABLE='TestDB-%' \\\n\tAWS_ACCESS_KEY_ID='dummy' \\\n\tAWS_SECRET_ACCESS_KEY='dummy' \\\n\tAWS_REGION='local' \\\n\tgo test -v -race ./... -cover -coverpkg=./...\n```\n\n### License\n\nBSD 2-Clause\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguregu%2Fdynamo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguregu%2Fdynamo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguregu%2Fdynamo/lists"}