freshcrate
Home > Uncategorized > gorm-query

gorm-query

A strongly-typed query builder and generic repository library

Description

A strongly-typed query builder and generic repository library

README

GORM Query 🚀

Read this in Chinese / 中文版

Go Reference Go Report Card License: MIT

GORM Query is a strongly-typed query builder and generic repository library built on top of GORM.

It eliminates fragile "magic strings" in GORM queries through code generation, providing a smooth fluent API experience. It also features enterprise-grade generic repositories and context-based transaction management.

✨ Core Features

  • 🛡️ Strongly-typed Query Building: Say goodbye to db.Where("age > ?", 18) and embrace UserProps.Age.Gte(18). Catch field name typos at compile time.
  • 📦 Out-of-the-box Generic Repository: Use repo.BaseRepository[T] to gain full CRUD capabilities with a single line of code.
  • 🎯 Stop Bloating Repositories: Combine the universal query builder to compose dynamic queries on the fly—no more writing dozens of FindByXxx methods.
  • 🔄 Implicit Context Transactions: Pass transactions via context.Context. Decouple your Service layer from the Repo layer without passing *gorm.DB everywhere.

📦 Installation

go get github.com/im-wmkong/gorm-query

🚀 Quick Start

1. Define Your Model

Define your GORM model as usual:

package model

import "gorm.io/gorm"

type User struct {
    gorm.Model
    UserName string `gorm:"column:user_name"`
    Email    string `gorm:"column:email"`
    Age      int    `gorm:"column:age"`
    Status   int    `gorm:"column:status"`
}

2. Configure and Run Code Generation

Create a simple generation script (e.g., at cmd/gen/main.go) and pass the models you want to generate properties for:

package main

import (
    "log"

    "your_project_name/model" // Replace with your actual project path
    "github.com/im-wmkong/gorm-query/genprops"
)

func main() {
    // Initialize the generator and provide your models
    err := genprops.New().GenerateAll([]any{
        model.User{},
    })
    
    if err != nil {
        log.Fatalf("generate failed: %v", err)
    }
}

Run the script from your terminal:

go run cmd/gen/main.go

This will automatically create a code file (e.g., props_gen.go) in your model directory containing the UserProps variable.

💡 Pro Tip: You can add //go:generate go run cmd/gen/main.go to the top of any Go file and trigger generation using go generate ./... in your standard workflow.

3. Enjoy Smooth Strongly-typed Queries

Now you can use the generated UserProps with the Query Builder for type-safe queries:

import (
    "your_project_name/model"
    "github.com/im-wmkong/gorm-query/query"
)

// 1. Build queries fluently
qb := query.New().
    Where(
        model.UserProps.Age.Gte(18),
        model.UserProps.UserName.Contains("wmkong"),
    ).
    Page(1, 20).
    Order(model.UserProps.ID.Desc())

// 2. Apply to gorm.DB
var users []model.User
err := qb.Apply(db).Find(&users).Error

💡 Advanced Usage

1. Generic Repository & Context-Aware Transactions

Combine db.Client and repo.BaseRepository to build a clean architecture:

Define Repositories and Service:

// Define UserRepository
type UserRepository struct {
    repo.BaseRepository[model.User]
}

func NewUserRepository(dbClient db.Client) *UserRepository {
    return &UserRepository{
        repo.New[model.User](dbClient),
    }
}

// Define ProfileRepository
type ProfileRepository struct {
    repo.BaseRepository[model.Profile]
}

func NewProfileRepository(dbClient db.Client) *ProfileRepository {
    return &ProfileRepository{
        repo.New[model.Profile](dbClient),
    }
}

// Define UserService
type UserService struct {
    userRepo    *UserRepository
    profileRepo *ProfileRepository
    tm          db.TransactionManager
}

func NewUserService(userRepo *UserRepository, profileRepo *ProfileRepository, tm db.TransactionManager) *UserService {
    return &UserService{
        userRepo:    userRepo,
        profileRepo: profileRepo,
        tm:          tm,
    }
}

Initialization and Injection:

import (
    "github.com/im-wmkong/gorm-query/db"
    "github.com/im-wmkong/gorm-query/repo"
)

// 1. Initialize DB Client
dbClient := db.NewClient(gormDB)
// 2. Instantiate Repositories
userRepo := NewUserRepository(dbClient)
profileRepo := NewProfileRepository(dbClient)
// 3. Inject into Service
userService := NewUserService(userRepo, profileRepo, dbClient)

Elegant Transaction Management in Service Layer:

// Business logic doesn't need to know about gorm.DB
func (s *UserService) CreateUserAndProfile(ctx context.Context, user *model.User, profile *model.Profile) error {
    // Transaction starts here
    return s.tm.Transaction(ctx, func(txCtx context.Context) error {
        // Automatically uses the transaction stored in txCtx
        if err := s.userRepo.Create(txCtx, user); err != nil {
            return err 
        }

        profile.UserID = user.ID
        // If this fails, the previous Create will automatically roll back
        if err := s.profileRepo.Create(txCtx, profile); err != nil {
            return err
        }
        
        return nil
    })
}

2. Dynamic Repository Queries

Stop inflating your Repository interfaces with dozens of specific methods like FindByNameAndAge. Use the query.Builder to handle dynamic conditions in the Service layer while keeping your Repository clean.

func (s *UserService) GetUsers(ctx context.Context, name string, minAge int) ([]*model.User, error) {
    // 1. Build dynamic conditions
    qb := query.New().Where(model.UserProps.Status.Eq(1))

    if name != "" {
        qb = qb.Where(model.UserProps.UserName.Contains(name))
    }
    if minAge > 0 {
        qb = qb.Where(model.UserProps.Age.Gte(minAge))
    }

    // 2. Pass the builder directly to the generic Find method
    return s.userRepo.Find(ctx, qb)
}

3. Query Reuse (Cloning)

Use .Clone() to derive new queries from a base query without polluting the original:

baseQuery := query.New().Where(UserProps.Status.Eq(1))

// Derived Query A
adults := baseQuery.Clone().Where(UserProps.Age.Gte(18))

// Derived Query B (Will NOT include Age >= 18 condition)
minors := baseQuery.Clone().Where(UserProps.Age.Lt(18))

🤝 Contributing

Issues and Pull Requests are welcome!

Before submitting, please run:

make tidy
make generate
make test

📄 License

This project is licensed under the MIT License.

Release History

VersionChangesUrgencyDate
v1.0.1Latest release: v1.0.1High4/13/2026
v1.0.0We are thrilled to announce the first official release of **GORM Query (v1.0.0)**! 🎉 在这个版本中,我们正式将团队内部打磨已久的 GORM 增强脚手架开源。GORM Query 致力于彻底解决 Go 开发者在使用 GORM 时遭遇的“魔法字符串易错”、“连表字段歧义”、“事务传递破坏架构”等痛点,为你带来堪比原生般丝滑、强类型安全的数据库操作体验。 ## ✨ 初版核心特性 (Initial Release Highlights) ### 🛡️ 1. 强类型查询构建器 (Type-Safe Query Builder) 再也不用手写脆弱的 `db.Where("age > ?", 18)`。配合专属的代码生成器,在编译期即可拦截字段拼写错误。 * 支持全套 SQL 操作:`Eq`, `Neq`, `Gt`, `Gte`, `Lt`, `Lte`, `In`, `Contains` 等。 * 支持 `.Clone()` 深度拷贝,安全派生查询条件,彻底告别切片底层污染。 #Medium3/13/2026

Dependencies & License Audit

Loading dependencies...

Similar Packages

alefGenerate fully-typed, lint-clean language bindings for Rust libraries across 11 languagesv0.4.4
ollamaGet up and running with Kimi-K2.5, GLM-5, MiniMax, DeepSeek, gpt-oss, Qwen, Gemma and other models.v0.21.0
go-apispecGenerate OpenAPI 3.1 specs from Go source code via static analysis — zero annotations, automatic framework detectionv0.4.7
goaDesign-first Go framework that generates API code, documentation, and clients. Define once in an elegant DSL, deploy as HTTP and gRPC services with zero drift between code and docs.v3.26.0
muxdAn open-source AI coding agent that lives in your terminal. Multi-provider, multi-channel, persistent sessions with git-like branching.v0.53.2