Skip to content

Latest commit

 

History

61 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Genus Logo

Genus ORM

The fastest type-safe ORM for Go

1.6x faster than GORM | 48% fewer allocations | Zero runtime query errors

Go Reference Go Report Card CI Status Coverage License: MIT Release

InstallationQuick StartBenchmarksDocsContributing


Why Genus?

// 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)

Performance Comparison

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 Comparison

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

Installation

go get github.com/go-genus/genus@latest

Requirements: Go 1.21+


Quick Start

1. Define your model

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"),
}

2. Connect and query

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))

Benchmarks

Methodology

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

Results

Query Performance (500 rows)

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

Single Row Lookup

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

Complex Query (5 conditions + ORDER BY + LIMIT)

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

Run Benchmarks

git clone https://github.com/go-genus/genus
cd genus
go test -bench=. -benchmem ./benchmarks/

Features

Core

  • Type-safe queries — Compiler catches typos and type mismatches
  • Direct []T return — No pointer gymnastics
  • Multi-database — PostgreSQL, MySQL, SQLite
  • Zero dependencies — Only Go standard library
  • Context-aware — All operations accept context.Context

Advanced

  • Relationships — HasMany, BelongsTo, ManyToMany
  • Eager loadingPreload("Posts.Comments")
  • Bulk mutations — Type-safe UPDATE/DELETE with WHERE
  • Typed time filtersAfter, Before, Between on time.Time columns
  • Soft deletes — Automatic deleted_at filtering
  • Hooks — BeforeCreate, AfterUpdate, etc.
  • Migrations — AutoMigrate + versioned migrations

Performance

  • 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

Enterprise

  • Sharding — Modulo and consistent hash strategies
  • OpenTelemetry — Distributed tracing
  • Audit logging — Automatic change tracking
  • Multi-tenancy — Row-level security

GenusUltra: Maximum Performance

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

Bulk Updates and Deletes

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))
// runs

Zero rows affected is a valid result, not an error.


Time Filters

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.


Relationships

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)

Documentation

Resource Description
Getting Started First steps with Genus
API Reference Complete API docs
Migration Guide Switching from GORM
Examples Working code samples

Contributing

git clone https://github.com/go-genus/genus
cd genus
./scripts/setup-hooks.sh
  1. Fork the repository
  2. Create your branch: git checkout -b feature/amazing
  3. Commit changes: git commit -m 'Add amazing feature'
  4. Push: git push origin feature/amazing
  5. Open a Pull Request

License

MIT License — see LICENSE


Built with performance in mind by the Go community

About

The Future of ORM with Golang

Resources

Contributing

Stars

18 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages