Quick Start
Open a database, sync a model, and run typed CRUD.
Oro is designed around three entry points:
db.Use[Product]() // model query
db.Table("products") // table query
db.Raw("select ...") // raw SQL
Model queries return structs. Table and raw queries return oro.Map by default and can be mapped to DTOs with MapTo[T]().
Install
go get github.com/duxweb/oro
During development this repository targets go1.27rc1.
source ~/.gvm/scripts/gvm && gvm use go1.27rc1
Define a model
package main
import oro "github.com/duxweb/oro"
type Product struct {
oro.Model
Code string
Price uint
Stock oro.Null[int]
}
func (Product) Define(s *oro.SchemaBuilder) {
s.Table("products")
s.Field("Code").String().Size(64).Unique()
s.Field("Price").Uint().Default(0)
s.Field("Stock").Int().Nullable()
}
Define is the schema source of truth. The struct stays clean, and database-specific details do not leak into tags.
Open and sync
package main
import (
"context"
"log"
oro "github.com/duxweb/oro"
"github.com/duxweb/oro/driver/sqlite"
)
func main() {
ctx := context.Background()
db, err := oro.Open(oro.Config{
Connections: map[string]oro.ConnectionConfig{
"default": {Driver: sqlite.Open("app.db")},
},
})
if err != nil {
log.Fatal(err)
}
defer db.Close(ctx)
if err := db.Register(Product{}); err != nil {
log.Fatal(err)
}
if err := db.Sync(ctx); err != nil {
log.Fatal(err)
}
}
Register accepts model values that implement Define. Sync creates missing tables, columns, and indexes. Destructive changes are rejected instead of being silently applied.
Create and query
created, err := db.Use[Product]().Create(ctx, &Product{
Code: "P001",
Price: 100,
Stock: oro.NullOf(10),
})
found, err := db.Use[Product]().Where("Code", "P001").First(ctx)
rows, err := db.Use[Product]().Where("Price", ">=", 100).OrderBy("ID").Get(ctx)
Return behavior is intentionally simple:
| Method | No rows |
|---|---|
First(ctx) |
nil, nil |
Find(ctx, id) |
nil, nil |
Get(ctx) |
empty slice |
Count(ctx) |
0, nil |
Update and delete
_, err = db.Use[Product]().Where("Code", "P001").Update(ctx, oro.Map{
"Price": 120,
"Stock": oro.NullZero[int](),
})
_, err = db.Use[Product]().Where("Code", "P001").Delete(ctx)
Update and Delete require a condition. This prevents accidental full-table writes.
Use a table directly
row, err := db.Table("products").Create(ctx, oro.Map{
"code": "P002",
"price": 200,
})
rows, err := db.Table("products").Where("price", ">=", 100).Get(ctx)
Table queries use database column names and return oro.Map.
type ProductView struct {
ID uint64
Code string
Price uint
}
view, err := db.Table("products").
Select("id", "code", "price").
MapTo[ProductView]().
First(ctx)