GORM CMD is a code generation tool for Go that produces type-safe query interfaces and field helper methods for GORM. It eliminates runtime query errors by verifying database operations at compile time.
- Type-safe Queries – Compile-time validation of database operations
- SQL Templates – Generate query methods directly from SQL template comments
- Field Helpers – Auto-generated, strongly typed field accessor methods
- Seamless GORM Integration – Works with existing GORM APIs out of the box
Requires Go 1.18+ (with generics).
go install gorm.io/cmd/gorm@latest
gorm gen -i ./query.go -o ./generated
import "your_project/generated"
func Example(db *gorm.DB, ctx context.Context) {
// Template-based query (from interface)
user, err := generated.Query[User](db).GetByID(ctx, 123)
// Field-based query (using generated helpers)
users, err := gorm.G[User](db).
Where(generated.User.Age.Gt(18)).
Find(ctx)
fmt.Println(user, users)
}
Write SQL/templating in interface method comments. Placeholders bind to method parameters automatically, and concrete, type‑safe implementations are generated.
type Query[T any] interface {
// SELECT * FROM @@table WHERE id=@id
GetByID(id int) (T, error)
// SELECT * FROM @@table WHERE @@column=@value
FilterWithColumn(column string, value string) (T, error)
// where("name=@name AND age=@age")
FilterByNameAndAge(name string, age int)
// SELECT * FROM @@table
// {{where}}
// {{if @user.Name }} name=@user.Name {{end}}
// {{if @user.Age > 0}} AND age=@user.Age {{end}}
// {{end}}
SearchUsers(user User) ([]T, error)
// UPDATE @@table
// {{set}}
// {{if user.Name != ""}} name=@user.Name, {{end}}
// {{if user.Age > 0}} age=@user.Age {{end}}
// {{end}}
// WHERE id=@id
UpdateUser(user User, id int) error
}
Usage notes (ctx auto‑injection): if a method signature doesn’t include ctx context.Context
, the generator adds it as the first parameter of the implementation.
import "your_project/generated"
func ExampleQuery(db *gorm.DB, ctx context.Context) {
// Get a single user by ID
user, err := generated.Query[User](db).GetByID(ctx, 123)
// Filter users by dynamic column and value
user, err := generated.Query[User](db).FilterWithColumn(ctx, "role", "admin")
// Filter users by name and age
users, err := generated.Query[User](db).FilterByNameAndAge("jinzhu", 25).Find(ctx)
// Conditional search using template logic
users, err := generated.Query[User](db).
SearchUsers(ctx, User{Name: "jinzhu", Age: 25})
// Update user with dynamic SET clause
err := generated.Query[User](db).
UpdateUser(ctx, updatedUser, 123)
}
Helpers are generated for “column‑like” fields on your models. These enable expressive, compile-time validated queries.
type User struct {
ID uint
Name string
Email string
Age int
Status string
CreatedAt time.Time
}
// Equality
generated.User.ID.Eq(1) // id = 1
generated.User.ID.Neq(1) // id != 1
generated.User.ID.In(1, 2, 3) // id IN (1, 2, 3)
// String
generated.User.Name.Like("%jinzhu%") // name LIKE '%jinzhu%'
generated.User.Name.IsNotNull() // name IS NOT NULL
// Numeric
generated.User.Age.Gt(18) // age > 18
generated.User.Age.Between(18, 65) // age BETWEEN 18 AND 65
// Nullable (Scanner/Valuer) types
generated.User.Score.IsNull() // score IS NULL (sql.NullInt64)
generated.User.LastLogin.IsNotNull() // last_login IS NOT NULL (sql.NullTime)
// ... more, see https://pkg.go.dev/gorm.io/cmd/gorm/field
// Simple filter
gorm.G[User](db).
Where(generated.User.Status.Eq("active")).
Find(ctx)
// Multiple conditions
gorm.G[User](db).
Where(generated.User.Age.Gt(18), generated.User.Status.Eq("active")).
Find(&users)
// Update using query helpers
gorm.G[User](db).
Where(generated.User.Status.Eq("pending")).
Update("status", "active")
// Update with Set: zero values + expressions (Assigner and SetExpr)
gorm.G[User](db).
Where(generated.User.Name.Eq("alice")).
Set(
generated.User.Name.Set("jinzhu"), // name = "jinzhu"
generated.User.IsAdult.Set(false), // is_adult = false (zero value)
generated.User.Score.Set(sql.NullInt64{}), // score = NULL (zero value)
generated.User.Age.Incr(1), // age = age + 1
generated.User.Age.SetExpr( // age = GREATEST(age, 18)
clause.Expr{SQL: "GREATEST(?, ?)", Vars: []any{clause.Column{Name: "age"}, 18}},
),
).
Update(ctx)
// Create with Set
gorm.G[User](db).
Set(
generated.User.Name.Set("alice"), // name = "alice"
generated.User.Age.Set(0), // age = 0
generated.User.IsAdult.Set(false), // is_adult = false
generated.User.Role.Set("active"), // role = "active"
).
Create(ctx)
Helpers are generated only for “column‑like” fields; associations are skipped.
- Included: all integers, floats,
string
,bool
,time.Time
,[]byte
, and any named type implementing one of:database/sql.Scanner
,database/sql/driver.Valuer
gorm.io/gorm.Valuer
,gorm.io/gorm/schema.SerializerInterface
- Excluded:
has one
,has many
,belongs to
,many2many
, and embedded slices/structs that represent relations
GORM CMD provides a SQL template DSL:
Directive | Purpose | Example |
---|---|---|
@@table |
Resolves to the model’s table name | SELECT * FROM @@table WHERE id=@id |
@@column |
Dynamic column binding | @@column=@value |
@param |
Maps Go params to SQL params | WHERE name=@user.Name |
{{where}} |
Conditional WHERE clause | {{where}} age > 18 {{end}} |
{{set}} |
Conditional SET clause (UPDATE) | {{set}} name=@name {{end}} |
{{if}} |
Conditional SQL fragment | {{if age > 0}} AND age=@age {{end}} |
{{for}} |
Iteration over a collection | {{for _, t := range tags}} ... {{end}} |
-- Safe parameter binding
SELECT * FROM @@table WHERE id=@id AND status=@status
-- Dynamic column binding
SELECT * FROM @@table WHERE @@column=@value
-- Conditional WHERE
SELECT * FROM @@table
{{where}}
{{if name != ""}} name=@name {{end}}
{{if age > 0}} AND age=@age {{end}}
{{end}}
-- Dynamic UPDATE
UPDATE @@table
{{set}}
{{if user.Name != ""}} name=@user.Name, {{end}}
{{if user.Email != ""}} email=@user.Email {{end}}
{{end}}
WHERE id=@id
-- Iteration
SELECT * FROM @@table
{{where}}
{{for _, tag := range tags}}
{{if tag != ""}} tags LIKE concat('%',@tag,'%') OR {{end}}
{{end}}
{{end}}
You don’t need any configuration to use the generator. For overrides, declare a package‑level genconfig.Config
in the package being generated — the generator will pick it up automatically.
package examples
import (
"database/sql"
"gorm.io/cmd/gorm/field"
"gorm.io/cmd/gorm/genconfig"
)
var _ = genconfig.Config{
// Override CLI -o for files in this package
OutPath: "examples/output",
// Map Go types to field helper types
FieldTypeMap: map[any]any{
sql.NullTime{}: field.Time{},
},
// Map `gen:"name"` names to helper types
FieldNameMap: map[string]any{
"date": field.Time{}, // map fields with `gen:"date"` tag to Time field helper
"json": JSON{}, // map fields with `gen:"json"` tag to custom JSON helper
},
// When true, apply only to current file instead of the whole package
FileLevel: false,
// Optional whitelists/blacklists (shell-style patterns):
// Whitelist takes priority: if Include* is non-empty, only those are generated,
// and Exclude* is ignored for that kind.
// Interfaces can be specified by pattern or by type-conversion form, e.g. models.Query(nil)
IncludeInterfaces: []any{"Query*", models.Query(nil)},
ExcludeInterfaces: []any{"*Deprecated*"},
// You can also specify struct types via type literal in the config file,
// e.g. models.User{} (treated as "models.User"), in addition to patterns.
IncludeStructs: []any{"User", "Account*", models.User{}},
ExcludeStructs: []any{"*DTO"},
}
- Declare Configuration
package examples
import "gorm.io/cmd/gorm/genconfig"
var _ = genconfig.Config{
OutPath: "examples/output",
FieldNameMap: map[string]any{
"json": JSON{}, // map fields with `gen:"json"` tag to custom JSON helper
},
}
- Declare JSON on the model using struct tags
package models
type User struct {
// ... other fields ...
// Tell the generator to use the custom JSON helper for this column
Profile string `gen:"json"`
}
- Define the JSON helper
// JSON is a field helper for JSON columns that generates different SQL for different databases.
type JSON struct{ column clause.Column }
func (j JSON) WithColumn(name string) JSON {
c := j.column
c.Name = name
return JSON{column: c}
}
// Equal builds an expression using database-specific JSON functions to compare
func (j JSON) Equal(path string, value any) clause.Expression {
return jsonEqualExpr{col: j.column, path: path, val: value}
}
type jsonEqualExpr struct {
col clause.Column
path string
val any
}
func (e jsonEqualExpr) Build(builder clause.Builder) {
if stmt, ok := builder.(*gorm.Statement); ok {
switch stmt.Dialector.Name() {
case "mysql":
v, _ := json.Marshal(e.val)
clause.Expr{SQL: "JSON_EXTRACT(?, ?) = CAST(? AS JSON)", Vars: []any{e.col, e.path, string(v)}}.Build(builder)
case "sqlite":
clause.Expr{SQL: "json_valid(?) AND json_extract(?, ?) = ?", Vars: []any{e.col, e.col, e.path, e.val}}.Build(builder)
default:
clause.Expr{SQL: "jsonb_extract_path_text(?, ?) = ?", Vars: []any{e.col, e.path[2:], e.val}}.Build(builder)
}
}
}
- Use it in queries
// This will generate different SQL depending on the database:
// MySQL: "JSON_EXTRACT(`profile`, "$.vip") = CAST("true" AS JSON)"
// SQLite: "json_valid(`profile`) AND json_extract(`profile`, "$.vip") = 1"
got, err := gorm.G[models.User](db).
Where(generated.User.Profile.Equal("$.vip", true)).Take(ctx)