Stop hand-writing SQL like it's 1999. This Go ORM gives your structs a life of their own with relationships, migrations, and zero drama.
- Type-safe: Fully leverages Go's type system with generics
- Zero drama: Simple, intuitive API with minimal boilerplate
- Relationships: Support for one-to-one, one-to-many, and many-to-many relationships
- Migrations: Easy schema migrations to evolve your database over time
- Multiple dialects: Support for SQLite, MySQL, and PostgreSQL
- Validation: Built-in validation using struct tags
- Transactions: First-class support for database transactions
- Hooks: Lifecycle hooks for entities (BeforeSave, AfterCreate, etc.)
- Query Builder: Fluent API for building complex queries
- Custom Queries: Support for raw SQL when needed
- No Code Generation: Uses reflection and generics instead of code generation
go get github.com/gooferOrm/gooferGoofer offers multiple approaches to fit your development style and project needs:
Build a full blog application from scratch - covers all features in a real-world context.
47,000+ word deep dive covering every aspect from basic usage to production deployment.
Simplified usage patterns for both high-level convenience and low-level control.
Complete guide to database migrations from development to production.
Understand which approach fits your project best.
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/mattn/go-sqlite3"
"github.com/gooferOrm/goofer/dialect"
"github.com/gooferOrm/goofer/repository"
"github.com/gooferOrm/goofer/schema"
)
// User entity
type User struct {
ID uint `orm:"primaryKey;autoIncrement"`
Name string `orm:"type:varchar(255);notnull"`
Email string `orm:"unique;type:varchar(255);notnull"`
}
// TableName returns the table name for the User entity
func (User) TableName() string {
return "users"
}
func main() {
// Open SQLite database
db, err := sql.Open("sqlite3", "./db.db")
if err != nil {
log.Fatalf("Failed to open database: %v", err)
}
defer db.Close()
// Create dialect
sqliteDialect := dialect.NewSQLiteDialect()
// Register entity
if err := schema.Registry.RegisterEntity(User{}); err != nil {
log.Fatalf("Failed to register User entity: %v", err)
}
// Get entity metadata
userMeta, _ := schema.Registry.GetEntityMetadata(schema.GetEntityType(User{}))
// Create table
userSQL := sqliteDialect.CreateTableSQL(userMeta)
_, err = db.Exec(userSQL)
if err != nil {
log.Fatalf("Failed to create users table: %v", err)
}
// Create repository
userRepo := repository.NewRepository[User](db, sqliteDialect)
// Create a user
user := &User{
Name: "John Doe",
Email: "john@example.com",
}
// Save the user
if err := userRepo.Save(user); err != nil {
log.Fatalf("Failed to save user: %v", err)
}
fmt.Printf("Created user with ID: %d\n", user.ID)
// Find the user by ID
foundUser, err := userRepo.FindByID(user.ID)
if err != nil {
log.Fatalf("Failed to find user: %v", err)
}
fmt.Printf("Found user: %s (%s)\n", foundUser.Name, foundUser.Email)
}The repository includes comprehensive examples demonstrating all features:
- Basic CRUD: Fundamental operations with SQLite
- Client Usage: Simplified high-level API
- Custom Queries: Advanced querying and aggregation
- Hooks & Lifecycle: Data transformation and audit logging
- Validation: Data integrity and error handling
- CLI Application: Complete command-line interface
- Todo App: Real-world application example
- MySQL: MySQL-specific features and configuration
- PostgreSQL: PostgreSQL integration and advanced features
- All Examples: Browse all examples on GitHub
- Examples Documentation: Detailed explanations
- Tutorial: Build a blog from scratch
To run any example:
cd examples/basic # or any other example
go run main.goGoofer ORM provides comprehensive documentation to help you master every aspect:
- Quick Start Guide: Get running in minutes
- Complete Tutorial: Build a real blog application
- Usage Flow Guide: Choose the right approach for your project
-
Comprehensive Guide: 47,000+ word complete reference
- Entity system and schema management
- Repository patterns and query building
- Relationships and eager loading
- Validation and lifecycle hooks
- Performance optimization
- Production best practices
-
Migration Guide: Complete migration system
- Schema versioning and evolution
- Production deployment strategies
- Zero-downtime migrations
- Rollback procedures
-
Client & Engine Guide: Usage patterns
- High-level client interface
- Low-level engine control
- Configuration and setup
- Testing strategies
- Entity System: Define your data models
- Repository Pattern: Type-safe database operations
- Relationships: Working with related data
- Validation: Data integrity and validation
- Hooks: Lifecycle events and automation
Entities are defined as Go structs with ORM tags:
type User struct {
ID uint `orm:"primaryKey;autoIncrement" validate:"required"`
Name string `orm:"type:varchar(255);notnull" validate:"required"`
Email string `orm:"unique;type:varchar(255);notnull" validate:"required,email"`
CreatedAt time.Time `orm:"type:timestamp;default:CURRENT_TIMESTAMP"`
Posts []Post `orm:"relation:OneToMany;foreignKey:UserID"`
}
func (User) TableName() string {
return "users"
}primaryKey: Marks the field as a primary keyautoIncrement: Enables auto-incrementing for the primary keytype:<type>: Specifies the database column typenotnull: Makes the column not nullableunique: Adds a unique constraintindex: Creates an index on the columndefault:<value>: Sets a default valuerelation:<type>: Defines a relationship (OneToOne, OneToMany, ManyToOne, ManyToMany)foreignKey:<field>: Specifies the foreign key fieldjoinTable:<table>: Specifies the join table for many-to-many relationshipsreferenceKey:<field>: Specifies the reference key for many-to-many relationships
The ORM integrates with the go-playground/validator package for validation:
required: Field cannot be emptyemail: Must be a valid email addressmin=<number>: Minimum value or lengthmax=<number>: Maximum value or lengthgte=<number>: Greater than or equal tolte=<number>: Less than or equal tooneof=<value1 value2...>: Must be one of the provided values
// Create a repository
repo := repository.NewRepository[User](db, dialect)
// Find by ID
user, err := repo.FindByID(1)
// Query builder
users, err := repo.Find().
Where("name LIKE ?", "%John%").
OrderBy("created_at DESC").
Limit(10).
Offset(0).
All()
// Count
count, err := repo.Find().Where("age > ?", 18).Count()
// Save (insert or update)
err := repo.Save(user)
// Delete
err := repo.Delete(user)
// Delete by ID
err := repo.DeleteByID(1)
// Transaction
err := repo.Transaction(func(txRepo *repository.Repository[User]) error {
// Operations within transaction
return nil
})
// Custom queries
var results []CustomStruct
rows, err := db.Query("SELECT * FROM users JOIN profiles ON users.id = profiles.user_id")
// Process rows...Implement these interfaces on your entities to hook into the entity lifecycle:
// Before/After Create
func (u *User) BeforeCreate() error {
u.CreatedAt = time.Now()
return nil
}
func (u *User) AfterCreate() error {
fmt.Printf("User %s created at %v\n", u.Name, u.CreatedAt)
return nil
}
// Before/After Update
func (u *User) BeforeUpdate() error {
u.UpdatedAt = time.Now()
return nil
}
// Before/After Save (applies to both create and update)
func (u *User) BeforeSave() error {
// Normalize data before saving
u.Email = strings.ToLower(strings.TrimSpace(u.Email))
return nil
}
// Before/After Delete
func (u *User) BeforeDelete() error {
// Log or perform cleanup before deletion
return nil
}Goofer provides a migration system to manage database schema changes:
// Create a migration manager
migrationManager := migration.NewManager(db, sqliteDialect)
// Register migrations
migrationManager.RegisterMigration("v1_to_v2", migrateV1ToV2)
// Define a migration function
func migrateV1ToV2(db *sql.DB, dialect dialect.Dialect) error {
// Add a new column
sql := fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s %s DEFAULT 0",
dialect.QuoteIdentifier("users"),
dialect.QuoteIdentifier("age"),
"INTEGER")
_, err := db.Exec(sql)
return err
}
// Run migrations
err := migrationManager.RunMigration("v1_to_v2")Implement the Validate() method on your entities for custom validation:
func (u *User) Validate() error {
// Create a validator
validator := validation.NewValidator()
// Validate using struct tags
errors, err := validator.ValidateEntity(u)
if err != nil {
return err
}
// Additional custom validation
if u.Role == "admin" && u.Age < 18 {
return fmt.Errorf("admin users must be at least 18 years old")
}
return nil
}- Indexes: Add appropriate indexes for frequently queried columns
- Batch Operations: Use transactions for batch operations
- Eager Loading: Preload related entities to avoid N+1 query problems
- Connection Pooling: Configure connection pooling appropriately for your workload
- Raw SQL: For complex queries, use raw SQL instead of the query builder
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.