The fastest type-safe ORM for Go
1.6x faster than GORM | 48% fewer allocations | Zero runtime query errors
Installation • Quick Start • Benchmarks • Docs • Contributing
// GORM: Runtime errors, no IDE help
var users []User
db.Where("nme = ?", "Alice").Find(&users) // Typo "nme" → discovered in production
// Genus: Compile-time safety, full autocomplete
users, _ := genus.Table[User](db).
Where(UserFields.Name.Eq("Alice")). // Typo? Won't compile.
Find(ctx)Benchmarked on Apple M4, Go 1.25, SQLite in-memory (run yourself):
| Metric | GORM | Genus | GenusUltra | Raw SQL |
|---|---|---|---|---|
| Select 500 rows | 2.7ms | 2.5ms | 1.7ms | 1.7ms |
| Memory allocations | 7,440 | 4,919 | 3,900 | 3,895 |
| Memory usage | 206 KB | 229 KB | 155 KB | 156 KB |
GenusUltra achieves raw SQL performance while providing full type safety.
| Feature | GORM | Ent | sqlc | Genus |
|---|---|---|---|---|
| Type-safe queries | ✓ | ✓ | ✓ | |
| Compile-time errors | ✓ | ✓ | ✓ | |
| No code generation required | ✓ | ✓ | ||
Direct []T return |
✓ | ✓ | ✓ | |
| Relationships & Preload | ✓ | ✓ | ✓ | |
| Raw SQL performance | ✓ | ✓ | ||
| Zero dependencies | ✓ | |||
| Learning curve | Low | High | Medium | Low |
go get github.com/go-genus/genus@latestRequirements: Go 1.21+
type User struct {
core.Model
Name string `db:"name"`
Email string `db:"email"`
IsActive bool `db:"is_active"`
}
var UserFields = struct {
Name query.StringField
Email query.StringField
IsActive query.BoolField
}{
Name: query.NewStringField("name"),
Email: query.NewStringField("email"),
IsActive: query.NewBoolField("is_active"),
}db, _ := genus.Open("postgres", "postgres://localhost/mydb")
// Type-safe queries with IDE autocomplete
users, err := genus.Table[User](db).
Where(UserFields.IsActive.Eq(true)).
Where(UserFields.Name.Like("A%")).
OrderByDesc("created_at").
Limit(10).
Find(ctx)
// CRUD operations
user := &User{Name: "Alice", Email: "alice@example.com"}
db.DB().Create(ctx, user)
db.DB().Update(ctx, user)
db.DB().Delete(ctx, user)
// Bulk UPDATE / DELETE, also type-safe
affected, err := genus.Table[User](db).
Where(UserFields.LastLogin.Before(cutoff)).
Update(ctx, UserFields.IsActive.Set(false))All benchmarks compare identical operations across ORMs:
- Dataset: 1,000 rows, querying 500 with WHERE clause
- Environment: Apple M4, Go 1.25, SQLite3 in-memory
- Iterations: 10 runs, averaged results
BenchmarkSelectAll_GORM-10 2.7ms ± 8% 206 KB 7,440 allocs
BenchmarkSelectAll_Genus-10 2.5ms ± 6% 229 KB 4,919 allocs
BenchmarkSelectAll_GenusUltra-10 1.7ms ± 4% 155 KB 3,900 allocs
BenchmarkSelectAll_RawSQL-10 1.7ms ± 5% 156 KB 3,895 allocs
BenchmarkSelectFirst_GORM-10 91µs ± 9% 4.6 KB 101 allocs
BenchmarkSelectFirst_Genus-10 78µs ± 7% 2.4 KB 71 allocs
BenchmarkSelectFirst_GenusUltra-10 63µs ± 5% 3.8 KB 51 allocs
BenchmarkSelectFirst_RawSQL-10 70µs ± 6% 1.0 KB 40 allocs
BenchmarkComplex_GORM-10 257µs ± 11% 10.3 KB 272 allocs
BenchmarkComplex_Genus-10 210µs ± 8% 10.5 KB 199 allocs
BenchmarkComplex_GenusUltra-10 183µs ± 6% 6.4 KB 128 allocs
BenchmarkComplex_RawSQL-10 193µs ± 9% 4.8 KB 103 allocs
git clone https://github.com/go-genus/genus
cd genus
go test -bench=. -benchmem ./benchmarks/- Type-safe queries — Compiler catches typos and type mismatches
- Direct
[]Treturn — No pointer gymnastics - Multi-database — PostgreSQL, MySQL, SQLite
- Zero dependencies — Only Go standard library
- Context-aware — All operations accept
context.Context
- Relationships — HasMany, BelongsTo, ManyToMany
- Eager loading —
Preload("Posts.Comments") - Bulk mutations — Type-safe
UPDATE/DELETEwithWHERE - Typed time filters —
After,Before,Betweenontime.Timecolumns - Soft deletes — Automatic
deleted_atfiltering - Hooks — BeforeCreate, AfterUpdate, etc.
- Migrations — AutoMigrate + versioned migrations
- GenusUltra — Zero-reflection scanning (raw SQL speed)
- Connection pooling — Configurable with presets
- Batch operations — Bulk INSERT/UPDATE/DELETE
- Query caching — LRU cache with TTL
- Read replicas — Automatic routing with round-robin
- Sharding — Modulo and consistent hash strategies
- OpenTelemetry — Distributed tracing
- Audit logging — Automatic change tracking
- Multi-tenancy — Row-level security
For performance-critical paths, GenusUltra eliminates reflection overhead:
// Register a zero-reflection scanner
func ScanUser(rows *sql.Rows) (User, error) {
var u User
err := rows.Scan(&u.ID, &u.Name, &u.Email, &u.IsActive)
return u, err
}
func init() {
query.RegisterScanFunc[User](ScanUser)
}
// Use UltraFastTable for raw SQL performance
users, _ := genus.UltraFastTable[User](db).
Select("id", "name", "email", "is_active").
Where(UserFields.IsActive.Eq(true)).
Find(ctx)Performance tiers:
| Builder | Speed | Allocations | Use Case |
|---|---|---|---|
Table[T]() |
Fast | Low | General purpose |
FastTable[T]() |
Faster | Lower | Reduced GC pressure |
UltraFastTable[T]() |
Raw SQL | Minimal | Hot paths |
Mutations across many rows keep the same compile-time guarantees as reads. The
SET clause is built from typed fields, so both the column name and the value
type are checked by the compiler:
// UPDATE users SET is_active = $1, updated_at = $2 WHERE last_login < $3
affected, err := genus.Table[User](db).
Where(UserFields.LastLogin.Before(cutoff)).
Update(ctx, UserFields.IsActive.Set(false))
// Soft delete when the model implements core.SoftDeletable, hard DELETE otherwise
removed, err := genus.Table[Session](db).
Where(SessionFields.ExpiresAt.Before(time.Now())).
Delete(ctx)
// Purge for good, including rows already soft-deleted
purged, err := genus.Table[Session](db).
Where(SessionFields.ExpiresAt.Before(retentionLimit)).
ForceDelete(ctx)updated_at is set automatically when the model has that column, and clearing a
nullable column is explicit via SetNull().
Mutations without a WHERE are refused. A forgotten filter is a wiped table,
so affecting every row has to be spelled out:
_, err := genus.Table[User](db).Update(ctx, UserFields.IsActive.Set(false))
// error: refusing to run UPDATE without a WHERE clause
_, err := genus.Table[User](db).AllowGlobal().Update(ctx, UserFields.IsActive.Set(false))
// runsZero rows affected is a valid result, not an error.
time.Time columns get a typed field with the comparison operators plus semantic
aliases, so date filtering never falls back to raw strings:
users, err := genus.Table[User](db).
Where(UserFields.CreatedAt.After(startOfMonth)).
Where(UserFields.CreatedAt.Before(time.Now())).
Find(ctx)
// Also: OnOrAfter, OnOrBefore, Between, In, NotIn, IsNull, IsNotNull
orders, err := genus.Table[Order](db).
Where(OrderFields.PlacedAt.Between(weekStart, weekEnd)).
Find(ctx)Values are normalized to UTC when the condition is built. This matters because
the SQLite driver serializes time.Time in the local timezone while SQLite
compares dates as text, which silently returns wrong results for any timezone
other than UTC. Normalization preserves the instant.
type User struct {
core.Model
Name string `db:"name"`
Posts []Post `db:"-" relation:"has_many,foreign_key=user_id"`
}
type Post struct {
core.Model
Title string `db:"title"`
UserID int64 `db:"user_id"`
User *User `db:"-" relation:"belongs_to,foreign_key=user_id"`
}
// Eager loading (avoids N+1)
users, _ := genus.Table[User](db).
Preload("Posts").
Find(ctx)| Resource | Description |
|---|---|
| Getting Started | First steps with Genus |
| API Reference | Complete API docs |
| Migration Guide | Switching from GORM |
| Examples | Working code samples |
git clone https://github.com/go-genus/genus
cd genus
./scripts/setup-hooks.sh- Fork the repository
- Create your branch:
git checkout -b feature/amazing - Commit changes:
git commit -m 'Add amazing feature' - Push:
git push origin feature/amazing - Open a Pull Request
MIT License — see LICENSE
Built with performance in mind by the Go community