MoonBit · ORM & SQL toolkit
The seam between your type and its table.
Types are contracts. The seam that stitches a MoonBit struct into SQL is the query — and moonorm sews it at compile time. A field that cannot map is not a runtime surprise; it is a compile error, caught on this very line.
id: Int64
idBIGINTprimary key
name: String
nameTEXTnot null
email: String
emailTEXTnot null unique
CREATE TABLE users (id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE)
Hover a field to lock its mapping. Click email to watch a type mismatch break the seam. Every type and every statement on the right is produced by this library, compiled to JavaScript.
The narrative
Four movements along one seam
A struct becomes a table
A query becomes SQL
where_, join, group_by each emit their fragment; values are bound, never interpolated.A schema diff becomes a plan
A session becomes work
The contract ledger
MoonBit type ↔ SQL type
The strong type and the weak type, reconciled. Rows that cannot map are marked — and explained.
| MoonBit | SQLite | PostgreSQL | MySQL | nullable |
|---|---|---|---|---|
| Int | INTEGER | INTEGER | INT | · |
| Int64 | INTEGER | BIGINT | BIGINT | · |
| Double | REAL | DOUBLE PRECISION | DOUBLE | · |
| String | TEXT | TEXT | TEXT | · |
| Bool | INTEGER | BOOLEAN | TINYINT(1) | · |
| Bytes | BLOB | BYTEA | BLOB | · |
| Int ↔ TEXT | needs an explicit kind=; the seam will not guess | no | ||
Side by side
What you write, what runs
let q = User::model()
.select()
.where_(fn(u) { u.name == "ada" })
.order_by("id", Asc)
.limit(10)
let (sql, params) = q.build()
// params: [Text("ada"), Int(10)]
SELECT id, name, email
FROM users
WHERE name = ?
ORDER BY id ASC
LIMIT ?
Placeholders are dialect tokens chosen by the driver (SQLite/MySQL ?, PostgreSQL $1). Values are bound out-of-band — injection is absent by construction.