_ _ ___ ___ __ __
| \| | / _ \ | _ \| \/ |
| .` || (_) || /| |\/| |
|_|\_| \___/ |_|_\|_| |_|
.-"""-.
/ _ \ [ norm: The Scalable Go ORM and Database Orchestrator For Postgres]
| (o)_(o)|
| (_) | + Automatic Routing
/ \ U / \ + R/W Separation
| \ `-' / | + Table Sharding
\___\___/___/ + Multi-Shard Relations with Integrated Joins and Cascade
Norm is a powerful, flexible ORM for Go that supports multiple database architectures including monolithic, read/write split, and sharded configurations. Built on PostgreSQL with pgx/v5, Norm provides automatic query routing, intelligent join handling, and seamless struct scanning.
Please see the benchmark here
- π Fluent Query Builder - Intuitive, chainable API for all CRUD operations
- π Multiple Database Modes - Monolithic, Read/Write Split, Sharding
- π Smart JOIN Support - Native, App-Side (skey), and Distributed joins
- π¦ Struct Scanning - Automatic mapping of query results to Go structs
- π― Auto Migrations - Automatic table creation from struct definitions
- β‘ Bulk Operations - Efficient bulk inserts with struct support
- π Foreign & Soft Keys - Full support for fkey and skey relationships
- π Sharding Support - Automatic routing across multiple database shards
- π Type Safety - Compile-time type checking with struct-based operations
go get github.com/skssmd/normpackage main
import (
"time"
"github.com/skssmd/norm"
)
type User struct {
ID uint `norm:"index;notnull;pk;auto"`
Email string `norm:"name:useremail;unique;notnull"`
Name string `norm:"name:fullname;notnull"`
Username string `norm:"name:uname;notnull;unique"`
Age *uint `norm:""`
CreatedAt time.Time `norm:"notnull;default:NOW()"`
}
type Order struct {
ID uint `norm:"index;notnull;pk;auto"`
UserID uint `norm:"index;notnull;fkey:users.id;ondelete:cascade"`
Total float64 `norm:"notnull"`
Status string `norm:"max:20;default:'pending'"`
CreatedAt time.Time `norm:"notnull;default:NOW()"`
}func main() {
// Register primary database
err := norm.Register("postgres://user:pass@localhost:5432/mydb").Primary()
if err != nil {
log.Fatal(err)
}
// Register tables
norm.RegisterTable(User{}, "users")
norm.RegisterTable(Order{}, "orders")
// Run auto migrations
norm.Norm()
}ctx := context.Background()
// INSERT
user := User{
Name: "Alice Williams",
Email: "alice@example.com",
Username: "alicew",
}
rowsAffected, err := norm.Table(user).Insert().Exec(ctx)
// SELECT with scanning
var users []User
err = norm.Table("users").
Select().
Where("created_at > $1", time.Now().AddDate(0, -1, 0)).
OrderBy("created_at DESC").
All(ctx, &users)
// UPDATE
rowsAffected, err = norm.Table("users").
Update("name", "Alice Updated").
Where("username = $1", "alicew").
Exec(ctx)
// DELETE
rowsAffected, err = norm.Table("users").
Delete().
Where("id = $1", 123).
Exec(ctx)type UserOrder struct {
UserName string `norm:"name:fullname"`
OrderTotal float64 `norm:"name:total"`
Status string `norm:"name:status"`
}
var userOrders []UserOrder
err := norm.Table("users", "id", "orders", "user_id").
Select("users.fullname", "orders.total", "orders.status").
Where("users.username = $1", "alice").
OrderBy("orders.created_at DESC").
All(ctx, &userOrders)
for _, order := range userOrders {
fmt.Printf("%s: $%.2f (%s)\n", order.UserName, order.OrderTotal, order.Status)
}- Database Connections - Setup primary, read, write, and sharded databases
- Model Definitions - Define your models with struct tags
- Table Registration - Register your models with Norm
- Auto Migrations - Automatically create tables from your models
- INSERT Operations - Single and bulk inserts, upserts
- SELECT Operations - Queries and struct scanning
- UPDATE Operations - Pair-based and struct-based updates
- DELETE Operations - Deletes and soft delete patterns
- JOIN Operations - Native, app-side, and distributed joins
- Raw SQL - Execute raw SQL queries
- Caching - Cache query results for faster access
Norm supports multiple database architectures:
- Global Monolith - Single primary database with optional replicas
- Read/Write Split - Separate pools for read and write operations
- Sharding - Distribute tables across multiple database shards
Norm automatically selects the optimal join strategy:
- Native JOIN - Standard SQL JOIN when tables are co-located
- App-Side JOIN - Application-level join for soft-key (skey) relationships
- Distributed JOIN - Cross-shard joins handled automatically
Map query results directly to Go structs with automatic field mapping:
var user User
err := norm.Table("users").
Select().
Where("id = $1", 123).
First(ctx, &user)users := []User{
{Name: "Alice", Email: "alice@example.com", Username: "alice"},
{Name: "Bob", Email: "bob@example.com", Username: "bob"},
}
rowsAffected, err := norm.Table("users").
BulkInsert(users).
Exec(ctx)user := User{Name: "John", Email: "john@example.com"}
rowsAffected, err := norm.Table(user).
Insert().
OnConflict("email", "update", "name", "updated_at").
Exec(ctx)// Register shards
norm.Register(dsn1).Shard("shard1").Primary()
norm.Register(dsn2).Shard("shard2").Primary()
// Assign tables to shards
norm.RegisterTable(User{}, "users").Primary("shard1")
norm.RegisterTable(Order{}, "orders").Primary("shard1")
norm.RegisterTable(Analytics{}, "analytics").Standalone("shard2")Check out the examples/ directory for complete working examples:
- Basic CRUD operations
- JOIN queries with scanning
- Sharding configurations
- Bulk operations
- Migration examples
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with pgx/v5 - High-performance PostgreSQL driver
- Inspired by modern ORM patterns and best practices
- π Documentation
- π Issue Tracker
- π¬ Discussions
Made with β€οΈ by the Norm team