ORM for Go (Golang): comparison and examples
Development in Go (Golang) often requires interaction with databases. Instead of writing raw SQL queries, developers increasingly turn to ORM (Object-Relational Mapping) — tools that automate the conversion of data between a relational database and language objects. In this article, we will cover the four most popular ORMs for Go: GORM, Ent, SQLBoiler, and Bun. You will learn their strengths and weaknesses, see code examples, and be able to make an informed choice for your project.
Why do you need an ORM in Go?
An ORM solves several key tasks:
- Reducing boilerplate code — no need to write CRUD operations manually.
- Type safety — the Go compiler checks that struct fields match table columns.
- Schema migrations — automatic creation and updating of tables.
- Database abstraction — switching DBMS (PostgreSQL, MySQL, SQLite) without rewriting queries.
1. GORM — the most popular choice
GORM is the de facto standard ORM in the Go ecosystem. It is known for its rich functionality: automatic migrations, hooks, method chaining, support for transactions and relationships (Has Many, Belongs To, Many2Many).
GORM code example
package main
import ( "gorm.io/driver/sqlite" "gorm.io/gorm")
type User struct { gorm.Model Name string Email string `gorm:"uniqueIndex"`}
func main() { db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) if err != nil { panic("failed to connect database") }
// Auto-migration db.AutoMigrate(&User{})
// Insert db.Create(&User{Name: "Alice", Email: "alice@example.com"})
// Read var user User db.First(&user, 1) // by primary key db.First(&user, "email = ?", "alice@example.com")
// Update db.Model(&user).Update("Name", "Bob")
// Delete db.Delete(&user)}Pros: huge community, extensive documentation, built-in support for associations and hooks.
Cons: sometimes slow performance on complex queries, magical behavior (e.g., automatic field modification).
2. Ent — code generation and strict typing
Ent from Facebook (Meta) is not just an ORM, but a whole framework for working with entities. It generates Go code based on a schema described in Go syntax. This provides maximum performance and type safety.
Ent code example
// schema/user.gopackage schema
import ( "entgo.io/ent" "entgo.io/ent/schema/field")
type User struct { ent.Schema}
func (User) Fields() []ent.Field { return []ent.Field{ field.String("name"), field.String("email").Unique(), }}
// main.gopackage main
import ( "context" "log"
"entgo.io/ent/dialect/sqlite" "yourproject/ent")
func main() { client, err := ent.Open("sqlite3", "file:ent.db?cache=shared&mode=rwc") if err != nil { log.Fatal(err) } defer client.Close()
ctx := context.Background() // Create tables if err := client.Schema.Create(ctx); err != nil { log.Fatal(err) }
// Insert u, _ := client.User.Create().SetName("Alice").SetEmail("alice@example.com").Save(ctx)
// Read user, _ := client.User.Get(ctx, u.ID) log.Println(user.Name)}Pros: high performance, full typing, excellent support for graph queries.
Cons: steep learning curve, need to run code generation, fewer built-in features (e.g., no hooks).
3. SQLBoiler —
Also in library
Machine Learning in Kotlin: Overview of Libraries for Data Science and ML
Machine Learning in PHP: Overview of Libraries and Tools
Data Validation in JavaScript (Node.js): Joi, Yup, Zod