{"id":32484417,"url":"https://github.com/waelson/go-spanner-repo","last_synced_at":"2025-10-27T03:51:55.170Z","repository":{"id":315944556,"uuid":"1061325503","full_name":"Waelson/go-spanner-repo","owner":"Waelson","description":"A lightweight toolkit for building repository layers on top of Google Cloud Spanner using Go.","archived":false,"fork":false,"pushed_at":"2025-09-21T20:18:36.000Z","size":64,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-21T20:28:30.891Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Waelson.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-21T17:31:04.000Z","updated_at":"2025-09-21T20:18:39.000Z","dependencies_parsed_at":"2025-09-21T20:29:26.511Z","dependency_job_id":"9db3a45c-e317-4536-a612-34f5acf1cc05","html_url":"https://github.com/Waelson/go-spanner-repo","commit_stats":null,"previous_names":["waelson/go-spanner-repo"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/Waelson/go-spanner-repo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Waelson%2Fgo-spanner-repo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Waelson%2Fgo-spanner-repo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Waelson%2Fgo-spanner-repo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Waelson%2Fgo-spanner-repo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Waelson","download_url":"https://codeload.github.com/Waelson/go-spanner-repo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Waelson%2Fgo-spanner-repo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281211332,"owners_count":26462075,"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","status":"online","status_checked_at":"2025-10-27T02:00:05.855Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2025-10-27T03:51:53.971Z","updated_at":"2025-10-27T03:51:55.165Z","avatar_url":"https://github.com/Waelson.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Generic Repository for Spanner Database\n\nA lightweight toolkit for building **repository layers** on top of **Google Cloud Spanner** using Go.  \nIt provides generic abstractions for CRUD, transactional operations, key-returning inserts, and pagination.\n\n---\n\n## ✨ Features\n\n- Generic typed repository (`repokit`) for domain entities\n- Flexible **row mapping** and **mutation building** functions\n- Standard CRUD operations:\n    - `FindByID`, `FindAll`, `FindByIDs`\n    - `Save`, `Update`, `Delete`\n- Key-returning inserts via DML (`SaveReturningKey`) — works with UUIDs or auto-incremented IDs\n- Transaction support (`SaveTx`, `DeleteTx`, `UpdateTx`, `SaveReturningKeyTx`)\n- Optional **cursor-based pagination** (no OFFSET required)\n\n---\n\n## 📦 Installation\n\n```bash\ngo get github.com/Waelson/go-spanner-repo\n```\n---\n\n## How to user\n### Create an entity\n```go\ntype User struct{\n\tUserID string\n\tEmail string\n}\n```\n\n### Implement specialized Repository\n```go\ntype UserRepository struct {\n    base *repokit.SpannerRepository[User]\n}\n\ntype UserKey struct {\n    ID string `spanner:\"user_id\"`\n}\n\nfunc (u *UserRepository) Save(ctx context.Context, user User) (User, error) {\n    err := u.base.Save(ctx, user)\n    return user, err\n}\n\nfunc (u *userNoTxRepository) Delete(ctx context.Context, userID string) error {\n    key := UserKey{ID: userID}\n    return u.base.Delete(ctx, key)\n}\n\nfunc (u *userNoTxRepository) Update(ctx context.Context, user User) error {\n    return u.base.Update(ctx, user)\n}\n\nfunc rowToUser(row *spanner.Row) (User, error) {\n    var u User\n    err := row.Columns(\u0026u.UserID, \u0026u.Email)\n    return u, err\n}\n\nfunc userToMutation(u domain.User) *spanner.Mutation {\n    return spanner.InsertOrUpdate(\n\t\tuserTable, \n\t\t[]string{\"user_id\", \"email\"}, \n\t\t[]interface{}{u.UserID, u.Email})\n}\n\nfunc NewUserRepository(spannerClient *spanner.Client) UserRepository {\n  base := repokit.NewBaseRepository[User](\n    spannerClient, \n    \"tb_users\", \n    []string{\"user_id\"},\n    rowToUser,\n    userToMutation,\n  )\n  return \u0026userTxRepository{base: base}\t\n}\n```\n### Consuming Repository\n```go\nfunc main(){\n  ctx := context.Background()\n  \n  // Create Spanner client\n  spannerClient, err := createSpannerClient(ctx)\n  if err != nil {\n    log.Fatal(err)\n  }\n\n  userRepository := repository.UserRepository(spannerClient)\n\n  // Create new user\n  user := domain.User{\n    UserID: uuid.New().String(), // Generate UUID for user ID\n    Email:  \"fake@email.com\",\n  }\n  \n  \n  // Insert user\n  user, err = userRepository.Save(ctx, user)\n  if err != nil {\n    log.Fatal(err)\n  }  \n  \n}\n```\n\n## 📖 API Overview\n\n| Method                                     | Description                              |\n| ------------------------------------------ | ---------------------------------------- |\n| `FindByID(ctx, key, columns)`              | Fetch entity by primary key              |\n| `FindAll(ctx, columns)`                    | Fetch all rows                           |\n| `FindByIDs(ctx, keys, columns)`            | Lookup multiple entities by key          |\n| `Save(ctx, entity)`                        | Insert or update (UPSERT)                |\n| `Update(ctx, entity)`                      | Update entity                            |\n| `Delete(ctx, key)`                         | Delete by primary key                    |\n| `SaveReturningKey(ctx, sql, params, dest)` | Insert with DML and return generated key |\n| `SaveTx`, `UpdateTx`, `DeleteTx`           | Transactional versions of mutations      |\n| `SaveReturningKeyTx`                       | Transactional key-returning insert       |\n| `Exists(ctx, key)`                         | Check if entity exists                   |\n\n---\n\n## ⚠️ Notes\n- Transactions: Use SaveTx, UpdateTx, DeleteTx inside a ReadWriteTransaction.\n- Key returning inserts: Requires DML (INSERT ... THEN RETURN). Works with GENERATE_UUID() or sequence-backed INT64.\n- Pagination: Implemented via cursor (pageToken = last seen PK). Avoids OFFSET for performance reasons.\n- Composite PKs: Supported; ensure you pass the struct with all primary key fields.\n\n---\n## 🧪 Testing\n```bash\ngo test ./repokit -v\n```\n---\n## 🤝 Contributing\nContributions are welcome!\nPlease open an issue or submit a PR with clear description and tests.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwaelson%2Fgo-spanner-repo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwaelson%2Fgo-spanner-repo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwaelson%2Fgo-spanner-repo/lists"}