package main
import (
"context"
"time"
"github.com/kintsdev/norm"
)
type User struct {
ID int64 `db:"id" norm:"primary_key,auto_increment"`
Email string `db:"email" norm:"unique,not_null,index,varchar(255)"`
Username string `db:"username" norm:"unique,not_null,varchar(50)"`
Password string `db:"password" norm:"not_null,varchar(255)"`
IsActive bool `db:"is_active" norm:"default:true"`
CreatedAt time.Time `db:"created_at" norm:"not_null,default:now()"`
DeletedAt *time.Time `db:"deleted_at" norm:"index"`
Version int64 `db:"version" norm:"version"`
}
func main() {
cfg := &norm.Config{Host: "127.0.0.1", Port: 5432, Database: "postgres"}
kn, _ := norm.New(cfg)
defer kn.Close()
// Auto-migrate schema from struct tags
_ = kn.AutoMigrate(&User{})
// Generic repository — full CRUD out of the box
repo := norm.NewRepository[User](kn)
_ = repo.Create(context.Background(), &User{Email: "[email protected]"})
}
From auto-migrations and fluent queries to transactions, read/write splitting, and circuit breakers — all in one lightweight package.
Schema migrations from struct tags. Creates tables, columns, indexes, foreign keys, renames, and type changes — all idempotent and transactional.
Chain Select/Where/Join/OrderBy/Limit, raw SQL, inserts with RETURNING, upserts, and keyset pagination.
Type-safe CRUD with NewRepository[T]. Bulk create, partial update, scopes, soft delete, and optimistic locking built in.
Soft delete by default with HardDelete() override. Optimistic locking via version tag prevents stale writes.
TxManager with transaction-bound QueryBuilder. Automatic commit/rollback with clean Go error handling.
Read/write splitting with transparent routing, exponential retry/backoff, and circuit breaker with open/half-open/closed states.
Use db and norm struct tags to define columns, indexes, foreign keys, and constraints.
Call norm.New(cfg) to connect, then AutoMigrate(&Model{}) to sync your schema.
Use the repository for CRUD, or the query builder for complex queries. Transactions, caching, and resilience are built in.
Chain methods to build complex queries without writing raw SQL. Supports joins, conditions DSL, keyset pagination, soft delete filtering, and more.
Select/Where/Join/OrderBy/Limit/Offset chainingEq/Ne/Gt/Lt/In/And/OrINSERT ... RETURNING and ON CONFLICT DO UPDATEAfter/Before cursors// Fluent query builder
var users []User
_ = kn.Query().
Table("users").
Where("is_active = ?", true).
OrderBy("id ASC").
Limit(10).
Find(ctx, &users)
// Repository with soft delete
repo := norm.NewRepository[User](kn)
_ = repo.Create(ctx, &user)
_ = repo.Delete(ctx, &user) // soft delete
_ = repo.HardDelete(ctx, &user) // permanent
// Upsert with ON CONFLICT
_ = kn.Query().
Table("users").
OnConflict("email").
DoUpdate("username", "updated_at").
Insert(ctx, &user)
type Post struct {
ID int64 `db:"id" norm:"primary_key,auto_increment"`
Title string `db:"title" norm:"not_null,varchar(200)"`
Slug string `db:"slug" norm:"not_null,unique:tenant_slug"`
Tenant int64 `db:"tenant_id" norm:"not_null,unique:tenant_slug"`
UserID int64 `db:"user_id" norm:"not_null,fk:users(id),on_delete:cascade"`
Amount float64 `db:"amount" norm:"type:decimal(20,8)"`
}
// Supported norm tokens:
// primary_key, auto_increment, unique, unique:group
// index, index:name, using:gin|btree|hash
// fk:table(col), on_delete:cascade|restrict
// not_null, default:expr, version
// type:decimal(20,8), varchar(50), text
// rename:old_column, collate:name
Define your entire database schema using Go struct tags. Primary keys, composites, indexes, foreign keys with actions, types, defaults, versioning — everything declarative.
decimal(20,8), citext, timestamptzBenchmarked on Apple M3 — go test -bench=. -benchmem. Near-zero allocation in hot paths.
StructMapper (cached)
0 alloc · 0 B/op
Keyset Pagination
6 allocs · 112 B/op
SELECT query build
10 allocs · 368 B/op
Placeholder conversion
1 alloc · 144 B/op
Production-ready PostgreSQL ORM with auto-migrations, fluent queries, generic repository, and enterprise resilience features. Open source, MIT licensed.