A Go utility library for database abstraction, HTTP routing, and logging.
- Requires Go 1.24+ (uses generics)
go get github.com/Carry-Rao/goutils| Module | Description | Docs |
|---|---|---|
| Database | SQL Database[T] / Table[T] over MySQL, PostgreSQL, and SQLite, plus a file-backed JSON backend. Conditions, transactions, slice/map fields, and portable unique-violation detection |
EN · Chinese |
| Cache | Key-value Table[T] over memory, Redis, and bloom filter, chainable via mixture |
EN · Chinese |
| HTTP Router | Prefix-tree router with typed path variables, middleware, per-pattern CORS, and correct 404/405 handling | EN · Chinese |
| Log | Buffered multi-level logging with color output | EN · Chinese |
Contributing: CONTRIBUTION.md · Chinese
import "github.com/Carry-Rao/goutils/database/sql/sqlite"
type User struct {
ID int `db:",primary"`
Name string
Tags []string `db:",child"` // stored in `users_tags`, read back automatically
}
db, err := sqlite.NewDatabase[User](map[string]string{"path": "./data.db"})
if err != nil {
panic(err)
}
defer db.Close()
// Creates the table and the companion table if they are missing.
tbl, err := db.GetTable("users")
if err != nil {
panic(err)
}
if err := tbl.Ins(User{ID: 1, Name: "Alice", Tags: []string{"vip"}},
database.Options{}); err != nil {
panic(err)
}
// []User, not []any. Conditions live in Options, and Tags comes back filled in.
users, err := tbl.Get(User{ID: 1}, database.Options{
Where: []database.Condition{database.Contains("Tags", "vip")},
Order: database.Ascending("Name"),
})
// WHERE from the old value, assignment from the new one. An empty Values means
// "replace the whole record".
err = tbl.Set(User{ID: 1}, User{ID: 1, Name: "Bob"}, database.Options{})
// Multi-table writes are atomic.
err = db.Tx(func(tx database.Tx[User]) error {
tt, err := tx.GetTable("users")
if err != nil {
return err
}
return tt.Del(User{ID: 1}, database.Options{})
})The three SQL backends are thin wrappers over a shared implementation: import the
one you want and call its NewDatabase. There is no factory and no TTL.
- SQL and cache are now separate packages.
databaseis SQL only (GetTable(name),Query,Exec,Tx,Close);database/cacheis key-value only (Create,GetTable(name, example), primary-key addressing). - No TTL anywhere. Expiry belongs in the layer above.
- All filtering and assignment moved into
Options, which replaceswhereFields/setFieldsand thettlargument:Table[T]:Ins(val, Options),Get(val, Options) ([]T, error),Set(old, updated, Options),Del(val, Options)cache.Table[T]:Ins(val),Get(val) ([]T, error),Set(old, updated),Del(val)
- Slice and map fields are ordinary fields. Mark them
db:",child"andIns/Get/Set/Delhandle them; there is noChildrentype and noAddChild/RemoveChild. - Queries no longer need raw SQL.
Options.Wherecovers comparisons,IN,BETWEEN,LIKE,IS NULL, and collection membership;Options.ValuesaddsUnion/Difference/Clear;Options.Skipexcludes columns. - A JSON backend was added: one file holds the whole database, keyed by table
and then by primary key, with collections nested inside each row. Non-key
conditions are a linear scan in Go. TLS settings are no longer hardcoded —
sslmode/tlscome from the config map and are omitted when unset. - 405 is now answered from a stub planted at registration time instead of by probing every method tree; a real handler always outranks a stub.
database/factorywas removed; backends are imported directly.router.BadRequestbecamerouter.MethodNotAllowed; status code 400 → 405- Router
:anywas removed;:stringis the only catch-all and does not span/
Covered by the test suite end to end: SQLite (including transactions, collection fields and set operations) and JSON (including atomic writes, rollback and linear-scan filtering). Connection strings for MySQL and PostgreSQL are covered by string-level tests; the live connections are not, since they need external services. The memory, bloom and mixture backends are covered too.
Not covered by automated tests: live MySQL, PostgreSQL and Redis connections, as they require external services. Not recommended for production use without your own testing.