Skip to main content
This guide walks you through building a small Go program that creates, reads, updates, and deletes records using GORM and SQLite.
1

Install GORM and a database driver

Install the GORM core library:
go get -u gorm.io/gorm
Then install a driver for your database. This quickstart uses SQLite, which requires no server to run.
go get -u gorm.io/driver/sqlite
SQLite is ideal for local development and testing. For production workloads, use MySQL, PostgreSQL, or SQL Server. See Connecting to a database for driver-specific DSN formats and configuration.
2

Define a model

A GORM model is a Go struct whose fields map to database columns. Embed gorm.Model to automatically get ID, CreatedAt, UpdatedAt, and DeletedAt fields.
import "gorm.io/gorm"

// gorm.Model provides:
//   ID        uint           `gorm:"primarykey"`
//   CreatedAt time.Time
//   UpdatedAt time.Time
//   DeletedAt gorm.DeletedAt `gorm:"index"`

type Product struct {
    gorm.Model
    Code  string
    Price uint
}
GORM uses the struct name to derive the table name (products for Product) and uses conventional field names for primary keys and timestamps.
You can define a model without embedding gorm.Model if you want full control over the schema. Any field named ID with a numeric type is treated as the primary key by convention.
3

Connect to a database

Use gorm.Open to initialize a database connection. Pass a dialector and an optional *gorm.Config.
import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
    panic("failed to connect database")
}
gorm.Open returns a *gorm.DB instance and an error. The *gorm.DB is safe for concurrent use and should be shared across your application — do not create a new instance per request.
By default, GORM pings the database immediately after opening to verify the connection. If your database is unavailable at startup, gorm.Open returns an error. Set DisableAutomaticPing: true in gorm.Config to skip this check.
4

Run AutoMigrate

AutoMigrate creates the table if it does not exist, and adds any missing columns or indexes. It never drops columns or modifies existing column types.
// Create the "products" table (or add missing columns to it)
err = db.AutoMigrate(&Product{})
if err != nil {
    panic("failed to migrate database")
}
You can pass multiple models at once:
db.AutoMigrate(&Product{}, &User{}, &Order{})
AutoMigrate is designed for development and CI environments. For production schema changes, prefer versioned migration tools such as golang-migrate or Atlas.
5

Create records

Pass a pointer to your model to db.Create. GORM sets the primary key and timestamps on the struct after the insert.
product := Product{Code: "D42", Price: 100}

result := db.Create(&product)
if result.Error != nil {
    panic(result.Error)
}

// product.ID is now set (e.g. 1)
// product.CreatedAt and product.UpdatedAt are set to the current time
fmt.Println("created product with ID:", product.ID)
To insert multiple records in one call, pass a slice:
products := []Product{
    {Code: "A10", Price: 50},
    {Code: "B20", Price: 75},
    {Code: "C30", Price: 120},
}

db.Create(&products)
6

Read records

Find the first record ordered by primary key:
var product Product

// Find by primary key
db.First(&product, 1)
// SELECT * FROM products WHERE id = 1 ORDER BY id LIMIT 1;

// Find by condition
db.First(&product, "code = ?", "D42")
// SELECT * FROM products WHERE code = 'D42' ORDER BY id LIMIT 1;
Find all records matching a condition:
var products []Product

// All products under $200
db.Where("price < ?", 200).Find(&products)
// SELECT * FROM products WHERE price < 200;

// All products (no filter)
db.Find(&products)
// SELECT * FROM products;
First raises gorm.ErrRecordNotFound when no record matches. Use errors.Is(result.Error, gorm.ErrRecordNotFound) to check. Find does not return an error for empty results — it returns an empty slice.
7

Update records

Update a single column with Update:
// Update the Price column on a specific record
db.Model(&product).Update("Price", 200)
// UPDATE products SET price=200, updated_at=<now> WHERE id=1;
Update multiple columns with Updates. Pass a struct (only non-zero fields are updated) or a map[string]interface{} (all keys are updated):
// Struct: only non-zero fields are written
db.Model(&product).Updates(Product{Price: 200, Code: "F42"})
// UPDATE products SET price=200, code='F42', updated_at=<now> WHERE id=1;

// Map: all keys are written regardless of zero value
db.Model(&product).Updates(map[string]interface{}{
    "Price": 0,
    "Code":  "F42",
})
// UPDATE products SET price=0, code='F42', updated_at=<now> WHERE id=1;
8

Delete records

Delete performs a soft delete when the model embeds gorm.Model (it sets DeletedAt instead of removing the row). Soft-deleted records are excluded from all future queries automatically.
// Soft delete — sets deleted_at on the row
db.Delete(&product, 1)
// UPDATE products SET deleted_at=<now> WHERE id=1;
To permanently delete a record, use Unscoped:
// Hard delete — removes the row from the table
db.Unscoped().Delete(&product, 1)
// DELETE FROM products WHERE id=1;

Complete working example

The following program ties all the steps together and runs against a local SQLite file:
package main

import (
    "errors"
    "fmt"

    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

type Product struct {
    gorm.Model
    Code  string
    Price uint
}

func main() {
    // 1. Open database connection
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        panic("failed to connect database")
    }

    // 2. Migrate schema
    db.AutoMigrate(&Product{})

    // 3. Create
    db.Create(&Product{Code: "D42", Price: 100})

    // 4. Read
    var product Product
    db.First(&product, 1)                 // find by primary key
    db.First(&product, "code = ?", "D42") // find by condition

    // 5. Update
    db.Model(&product).Update("Price", 200)
    db.Model(&product).Updates(Product{Price: 200, Code: "F42"})
    db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})

    // 6. Delete (soft delete — sets deleted_at)
    db.Delete(&product, 1)

    // Check for record not found
    var p Product
    result := db.First(&p, 999)
    if errors.Is(result.Error, gorm.ErrRecordNotFound) {
        fmt.Println("record not found")
    }
}

Next steps

Connecting to a database

Configure MySQL, PostgreSQL, SQLite, and SQL Server with real DSN examples and connection pool settings.

Declaring models

Define models with custom column names, data types, constraints, and embedded structs.

CRUD: Create

Batch inserts, upserts, hooks, and creating with associations.

CRUD: Query

Where conditions, ordering, pagination, preloading associations, and raw SQL.

Build docs developers (and LLMs) love