Templates basic CRUD operations using Go Generics.
legal | ||
context.go | ||
creator.go | ||
deleter.go | ||
doc.go | ||
getter.go | ||
go.mod | ||
go.sum | ||
LICENSE | ||
lister.go | ||
Makefile | ||
README.md | ||
updater.go |
gorm-crud
Mostly developed for me. Templates basic CRUD operations using Go Generics.
Usage
go get go.pitz.tech/gorm/crud
Basic
The basic usage of this library is as follows. This example demonstrates usage for a single resource type, however this pattern can apply to multiple resources.
package main
import (
"gorm.io/gorm"
"go.pitz.tech/gorm/crud"
)
// Resource is an annotated database model.
type Resource struct{}
// ResourceDB provides an abstraction for managing Resource data in the associated gorm.DB.
type ResourceDB struct {
List crud.ListFunc[Resource]
Create crud.CreateFunc[Resource]
Get crud.GetFunc[Resource]
Update crud.UpdateFunc[Resource]
Delete crud.DeleteFunc
}
func main() {
// todo: open your database
var db *gorm.DB
resources := &ResourceDB{
List: crud.Lister[Resource](db),
Create: crud.Creator[Resource](db),
Get: crud.Getter[Resource](db),
Update: crud.Updater[Resource](db),
Delete: crud.Deleter[Resource](db),
}
// data, err := resources.List(ctx, 0, 10)
}
Transactions
Directly managing transactions is particularly useful when you want to manage multiple resources in a single operation.
package main
import (
"context"
"gorm.io/gorm"
"go.pitz.tech/gorm/crud"
)
type Resource1 struct {}
type Resource2 struct {}
type Resource1DB struct {
Create crud.CreateFunc[Resource1]
}
type Resource2DB struct {
Create crud.CreateFunc[Resource2]
}
func main() {
// todo: open your database
var db *gorm.DB
var resource1db *Resource1DB
var resource2db *Resource1DB
// optionally specify transaction options
txn := db.Begin()
defer txn.Rollback()
ctx := crud.Transaction(context.Background(), txn)
err := resource1db.Create(ctx, &Resource1{})
if err != nil {
// resource 1 and 2 do not exist!
return
}
err = resource2db.Create(ctx, &Resource2{})
if err != nil {
// resource 1 and 2 do not exist!
return
}
err = txn.Commit().Error
if err != nil {
// resource 1 and 2 do not exist!
return
}
// resource 1 and 2 exist!
}
Extending
These methods can easily be customized by providing concrete method definitions in the structure themselves. For
example, suppose a UserDB
wanted to support a GetByEmail
operation. Such a method might explicitly ask for the email
address and call a Get
function with the explicit field as a filter. For instance:
func (db *UserDB) GetByEmail(ctx context.Context, email string) (*User, error) {
return db.Get(ctx, map[string]interface{}{
"email": email,
})
}
License
MIT
. See LICENSE for more details.