Skip to content

GORM

Databases & CachingDatabase/ORMGo

What it is

GORM is the most widely used ORM for Go, offering associations, hooks, migrations, transactions and eager loading over a struct-based model definition.

Models are plain structs with optional tags. GORM handles CRUD, associations, soft deletes, migrations and transactions, and can log the SQL it generates.

Installation

go get -u gorm.io/gorm

Getting started

The smallest useful thing you can do with it, and what each part means.

Model, migrate, query
type Book struct {
    gorm.Model              // ID, CreatedAt, UpdatedAt, DeletedAt
    Title    string `gorm:"size:200;not null;index"`
    AuthorID uint
    Author   Author
}

db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil { return err }

db.AutoMigrate(&Book{}, &Author{})

var books []Book
db.WithContext(ctx).
    Where("title LIKE ?", "%Dune%").
    Limit(10).
    Find(&books)
Embedding gorm.Model adds the conventional columns including a soft-delete timestamp — note that DeletedAt means Find silently excludes soft-deleted rows.
Preloading associations
// Without Preload this issues one query per book — the N+1 problem.
var books []Book
db.Preload("Author").Find(&books)

// Nested, and with a condition on the association.
db.Preload("Author.Country").
   Preload("Reviews", "rating > ?", 4).
   Find(&books)
Preload issues one extra query per association rather than a join, which is usually the right trade. Forgetting it is the most common GORM performance bug.

Advanced usage

Where the library earns its place over a simpler alternative.

Transactions with rollback
err := db.Transaction(func(tx *gorm.DB) error {
    if err := tx.Create(&order).Error; err != nil {
        return err // any returned error rolls the whole thing back
    }
    if err := tx.Model(&Stock{}).
        Where("sku = ? AND quantity >= ?", sku, qty).
        Update("quantity", gorm.Expr("quantity - ?", qty)).Error; err != nil {
        return err
    }
    return nil
})
Using gorm.Expr keeps the decrement in SQL so it is atomic; reading the value into Go and writing it back would race with concurrent orders.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

record not found
First and Last return gorm.ErrRecordNotFound when nothing matches. Test with errors.Is(err, gorm.ErrRecordNotFound); Find returns an empty slice and no error instead.
Updates silently skip zero values
Struct-based Updates ignores zero values such as false, 0 and "". Use a map[string]interface{} or Select the columns explicitly.

Best practices

  • Always pass a context with WithContext so queries are cancelled with the request.
  • Use Preload for associations you will read — the N+1 problem is GORM's most common performance issue.
  • Check .Error on every chain; GORM does not panic on failure, it records the error.
  • Enable the logger in development to see the SQL actually being generated.

Background

Why it exists, and what it was reacting to.

Created by Jinzhu, GORM brought a full-featured ORM to a community that had mostly written SQL by hand. Its v2 rewrite in 2020 added context support, prepared-statement caching and a much faster core.