go-gorm/gen: a type-safe query layer generated from your database schema
Gen: Friendly & Safer GORM powered by Code Generation
At a glance
- What is it?
- Gen reads a live database and writes Go DAO code for GORM, trading runtime reflection for compile-time field access. It is a good fit for services that already run GORM and want their queries checked by the compiler.
- Who is it for?
- Adopt go-gorm/gen if you already run GORM and want column names, operators and result types checked at compile time instead of at query time. Do not adopt it if your team cannot commit to re-running the generator whenever the schema changes, because generated code that lags behind the database is worse than hand-written code that lags behind it.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 12 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 26, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What problem go-gorm/gen solves, and for whom
Plain GORM queries are built from strings and struct tags. A column rename does not break the build, it breaks at runtime, and usually in production. Gen's answer is to read the database schema and emit Go code that names every table, every column and every condition builder, so the compiler catches the mismatch first.
The audience is narrow and specific. You need to be on GORM already, because Gen is built on top of it and reuses its plugins, dialectors and ecosystem. You need a schema that lives in a real database, since the generator inspects it rather than parsing migration files. And you need to be willing to check generated code into the repository, because the README's recommended layout puts generated model structs under internal/dal/model and generated query code under internal/dal/query, both committed.
If your project is a small script with three queries, this is overhead with no payoff. The value shows up when a codebase has dozens of tables and several engineers who all need to agree on what a column is called.
How generation works: schema in, DAO package out
The mechanism is a two-phase pipeline. First, gen.NewGenerator builds a generator from a gen.Config value. You hand it a live *gorm.DB via UseDB, and it reads table metadata from that connection. Second, ApplyBasic registers which tables should become models, and Execute writes the files to OutPath.
The README's baseline call is g.ApplyBasic(g.GenerateAllTable()...), which registers every table the connection can see. You can narrow that by passing g.GenerateModel("users") for specific tables, which is also how interface methods get bound to a table.
Database-to-struct conversion follows GORM conventions, so tags for nullable, default, unsigned, index and type come out the way GORM expects them. Config flags control the shape of the output: FieldNullable, FieldCoverable and FieldWithIndexTag affect the generated struct tags, and FieldWithIndexTag in particular is what makes index metadata visible in the model.
The generated query package exposes each table as a variable, so query.User gives you a field set you can build conditions from. That is the whole point of the design: u.Age.Gt(18) is a method call on a generated field object, not a string, so a typo is a compile error.
Installing go-gorm/gen and running a first generation
Install it as a library. The README gives this command, and it is the only install step.
go get gorm.io/gen@latestNext, create the generator entry point. The README recommends cmd/gen/main.go, and the important detail is that it opens a real database connection, because the generator reads the schema from it.
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gen"
"gorm.io/gorm"
)
func main() {
db, err := gorm.Open(mysql.Open("dsn"), &gorm.Config{})
if err != nil {
panic(err)
}
g := gen.NewGenerator(gen.Config{
OutPath: "internal/dal/query",
Mode: gen.WithDefaultQuery | gen.WithQueryInterface,
})
g.UseDB(db)
g.ApplyBasic(g.GenerateAllTable()...)
g.Execute()
}Run it, and you should see Go files appear under internal/dal/query.
go run ./cmd/genThe Mode flags matter more than they look. WithDefaultQuery is what enables query.SetDefault(db), a one-time global registration. If you set it, call SetDefault once during service startup, then use query.User directly.
query.SetDefault(db)
u := query.User
_, _ = u.WithContext(context.Background()).
Where(u.Age.Gt(18)).
Find()If you would rather not have a package-level global, drop WithDefaultQuery and use query.Use(db) to get a query handle scoped to one connection. The README presents both, and the second is the safer default for libraries and tests.
Interface SQL templates and the generics mode
Two features go beyond plain schema-to-struct generation, and both change how much code you write by hand.
The first is interface SQL templates. You declare a Go interface whose methods carry SQL in comments, then bind it to a table with g.ApplyInterface. The README's example defines FindByID with a SELECT * FROM users WHERE id=@id body, and FindByOptionalName with a {{where}} block that conditionally includes name=@name only when the argument is non-empty. The generator turns those into typed methods on the generated query object. This is the pragmatic escape hatch: complex SQL stays readable as SQL, but the call site is still typed. The template syntax is documented by example in the test corpus at tests/diy_method/method.go, not in the README, so plan on reading source to learn the full grammar.
The second is generics mode, enabled with gen.WithGeneric. The README is explicit that this is not a separate workflow, it changes the generated query API surface for stronger typing. If your module targets Go 1.18 or later, this is the mode you probably want, but it changes the signatures your application code calls, so switching later is a refactor rather than a config flip.
There is also Config.UseAny, which emits any instead of interface{} in generated code. The README notes a side effect that deserves attention: it rewrites the literal text interface{} in comments and string literals too. If any of your generated documentation contains that string, it will be altered.
Where the generated-code approach costs you
The main limitation is the one the design implies. The generator reads a live database, so the generated code is only as current as the last time someone ran it. Nothing in the README describes a staleness check, a CI gate, or a way for the build to fail when the schema has moved on. You have to build that yourself, and until you do, the compile-time safety is theoretical: the compiler checks your code against yesterday's schema.
Second, the generated output depends on the connection you give it. The README's example uses the MySQL driver, and the repository's go.mod lists gorm.io/driver/mysql only as an indirect dependency. Dialect-specific behaviour in the generated model tags will follow whatever database you point it at, so a team running MySQL in production and SQLite in tests should expect the generated code to reflect whichever one the generator connected to.
Third, the README does not document rollback, and it does not describe what Execute does to files it previously wrote when a table is dropped or renamed. Treat the output directory as generated and regenerable, and keep it under version control so a bad run shows up as a diff rather than a surprise.
Finally, the query DSL covers fields, conditions and assignments. Joins, subqueries and upserts are common questions about this project, and the README does not address them directly. For those, you may end up writing raw SQL through the interface template mechanism rather than composing it from generated field objects.
GenTool, and how this differs from ent
If you do not want a checked-in Go program as your generator, the repository ships a CLI. The README points to tools/gentool/README.md, with a Chinese version at tools/gentool/README.ZH_CN.md, and the release list includes tools/gentool/v0.0.3. The trade-off is familiar: a CLI is easier to wire into a build step, while a checked-in generator program is easier to review and to customize per project.
The natural comparison is ent, which also generates typed Go code from a schema. The difference in approach is where the schema lives. Ent defines entities in Go code and generates both the schema migrations and the query API from that definition. Gen goes the other direction: the database is the source of truth, and the generated code follows it. That makes Gen a better fit when the schema is owned by another team, by a DBA, or by an existing production database you cannot restate in Go. It makes ent a better fit when you want the schema itself to live in the repository and be versioned with the application.
A second difference is inheritance. Gen is built on GORM, so you keep the same plugins, dialectors and ecosystem. If you are already running GORM with dbresolver or hints, Gen slots in without a second data layer. Ent is its own runtime.
Maintenance, licence, and what to pin
The repository is not archived, and the last push was on 2026-08-25, which is recent enough that the project is being changed. Releases follow a v0.3.x line, with v0.3.29 published on 2026-08-25 and v0.3.28 on 2026-06-02. A pre-1.0 version number means the API is not promised as stable, so pin the version in go.mod rather than tracking latest in CI.
The go.mod declares go 1.22.0 and depends on gorm.io/gorm v1.31.2, golang.org/x/tools v0.26.0 and golang.org/x/exp. Because Gen is a code generator, its dependency on golang.org/x/tools means a Go toolchain upgrade can require a Gen upgrade, which is worth knowing before you pin an old version and then move the toolchain forward.
The licence is MIT, which permits commercial and closed-source use and requires the licence text and copyright notice to be preserved. That is a plain statement of what the licence says, not legal advice; if your organisation has a policy review for dependencies, run it.
On upgrade cost: because output is generated, a version bump can change the generated files themselves. Review the diff in internal/dal/query after each bump the way you would review hand-written code, and check the release notes for the version you are moving to.
Editorial conclusion
Adopt go-gorm/gen if you already run GORM and want column names, operators and result types checked at compile time instead of at query time. Do not adopt it if your team cannot commit to re-running the generator whenever the schema changes, because generated code that lags behind the database is worse than hand-written code that lags behind it. Before committing, verify three things: that the generator runs against your target dialect (the README's example uses the MySQL driver), that your CI pipeline has a step that fails when generated output is stale, and that the Mode flags you pick match how your services share a connection. The generator entry point belongs in the repository under cmd/gen/main.go, checked in, so the code that produces your DAO layer is reviewable alongside the code it produces.
Frequently asked questions
What is golang GORM?
The README treats GORM as the ORM that Gen is built on top of, reusing the same plugins, dialectors and ecosystem. It does not define GORM beyond that.
What are the key differences between GORM and ent?
The README does not compare them. From the repository layout, Gen generates code from a live database schema and runs on top of GORM, while ent is a separate runtime that generates from entities defined in Go.
What does the name GORM mean?
The README does not explain the name. It only refers to GORM as the library Gen is built on and links to the GORM Guides at gorm.io/docs.
Is GORM an ORM?
The README describes Gen as being built on top of GORM and reusing its plugins and dialectors, which places GORM as the ORM layer underneath. It does not define GORM further.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/go-gorm-gen)