Ultra-lightweight, strongly-typed ORM engineered for WebAssembly and backend environments.
- Declarative Source of Truth: Hand-written
model.Definitionliterals as the source for code generation. - Zero Reflection: Interface-driven schema via
github.com/tinywasm/modelwith generated code. - Isomorphic: Same generated code works in Go (backend) and WASM (frontend).
- 0-alloc Codec: Symmetric, reflection-free serialization/deserialization methods generated for every model.
- Query Builder: Reflection-free, type-safe query building.
- Backend-Agnostic:
orm.DBwraps astorage.Conn— no storage contract is defined here, so any backend implementingtinywasm/storage(Postgres, SQLite, in-memory, IndexedDB, ...) works unchanged.
This repository is the ergonomic layer only — the equivalent of database/sql. It defines no storage contract of its own. Other parts of the ecosystem:
| Component | Repository | Role |
|---|---|---|
| storage | tinywasm/storage | Storage port (Executor/Compiler/Query/Condition/Plan, mock, mem) — the equivalent of database/sql/driver. A backend implements storage.Conn; orm.New(conn) takes one. |
| ormc | tinywasm/ormc | Build-time code generator. |
| ddlc | tinywasm/ddlc | SQL Schema (DDL) exporter and utilities. |
| sqlmcp | tinywasm/sqlmcp | MCP tool provider for LLM interaction. |
go get github.com/tinywasm/ormTo install the code generator:
go install github.com/tinywasm/ormc/cmd/ormc@latestIn this ORM, you write the typed definition of your model, and ormc generates the concrete struct and the rest of the code. This ensures your schema is a compile-time checked symbol, eliminating errors from fragile string tags.
Create a model.go or models.go file. Define your models as model.Definition variables ending in Model.
package user
import (
"github.com/tinywasm/model"
)
var UserModel = model.Definition{
Name: "user",
Fields: model.Fields{
{Name: "id", Type: model.Int(), DB: &model.FieldDB{PK: true, AutoInc: true}},
{Name: "name", Type: model.Text(), NotNull: true, Permitted: model.Permitted{Minimum: 2}},
{Name: "email", Type: model.Text(), NotNull: true, DB: &model.FieldDB{Unique: true}},
{Name: "bio", Type: model.Text(), OmitEmpty: true},
{Name: "addr", Type: model.Struct(), Ref: &AddressModel},
},
}
var AddressModel = model.Definition{
Name: "address",
Fields: model.Fields{
{Name: "street", Type: model.Text()},
{Name: "city", Type: model.Text()},
},
}ormcGenerates model_orm.go next to each source file, containing the type User struct, type Address struct, and all interface implementations.
func GetUser(db *orm.DB, id int64) (*user.User, error) {
return user.ReadOneUser(
db.Query(&user.User{}).Where("id").Eq(id),
&user.User{},
)
}| Field | Meaning |
|---|---|
Name |
wire/DB column name (snake_case). Becomes PascalCase in the generated struct. |
Type |
model.Kind (e.g. model.Text(), model.Int(), model.Struct()). |
NotNull |
NOT NULL constraint in DB. |
OmitEmpty |
Skips field if zero-value in codecs. |
Exclude |
Field exists in generated struct but is skipped in Schema(), Pointers(), and codecs (e.g. for password_hash). |
Widget |
Binding for UI rendering (see tinywasm/form/input). |
DB |
Database-specific metadata (PK, Unique, AutoInc, RefColumn, OnDelete). |
Ref |
Points to another *model.Definition. Used for composition or scalar Foreign Keys. |
Permitted |
Validation rules (Minimum, Maximum, Letters, Numbers, etc.). |
- Composition: When
Typeismodel.Struct()ormodel.StructSlice(). The field becomes a nested struct or slice of structs part of the same row/JSON. - Scalar Foreign Key: When
Typeis a scalar (e.g.model.Int()) andRefis non-nil. Adds DDL metadata forFOREIGN KEYconstraints.
{Name: "user_id", Type: model.Int(), Ref: &UserModel, DB: &model.FieldDB{RefColumn: "id", OnDelete: "CASCADE"}}model.Kind |
Generated Go Type | Codec Method |
|---|---|---|
model.Text(), model.Raw() |
string |
String / Raw |
model.Int() |
int64 |
Int |
model.Float() |
float64 |
Float |
model.Bool() |
bool |
Bool |
model.Blob() |
[]byte |
Bytes |
model.IntSlice() |
[]int |
Array |
model.Struct() |
T (from Ref) |
Object |
model.StructSlice() |
[]*T (from Ref) |
Array |
The ormc generator produces:
type T structdeclaration.Schema() []model.Field,Pointers() []any(parallel, zero-alloc).EncodeFields(model.FieldWriter),DecodeFields(model.FieldReader)(symmetric codec).IsNil() bool.Validate(action byte) error(callingmodel.ValidateFields). Always generated.ModelName() string.ReadOneT(),ReadAllT(),TListtype (for DB models).SchemaExt() []ddlc.FieldExt(requires tinywasm/ddlc).