Grit Docsv3.114.0

Migrations & Seeding

Database migrations with GORM AutoMigrate and seeding with initial data.

Overview

Grit uses GORM AutoMigrate for database schema management. Migrations run automatically on API startup and can also be triggered manually via the CLI. Seeding populates the database with initial data for development and production.

Migrations

How It Works

GORM AutoMigrate creates tables, adds missing columns, and creates indexes based on your Go model definitions. It does not delete columns or drop tables — it's additive only.

All models are registered in apps/api/internal/models/user.go via the AutoMigrate function:

func AutoMigrate(db *gorm.DB) error {
    models := []interface{}{
        &User{},
        &Upload{},
        // grit:models  <-- new models injected here by grit generate
    }

    for _, model := range models {
        if err := db.AutoMigrate(model); err != nil {
            log.Printf("Warning: migration error for %T: %v (skipping)", model, err)
        }
    }

    return nil
}

Running Migrations

Migrations run automatically when the API starts. You can also run them manually:

# Run migrations
grit migrate

# Drop all tables and re-migrate (development only!)
grit migrate --fresh

Fresh Migrations

The --fresh flag drops all tables in the database and re-creates them from scratch. This is useful during development when you've made breaking schema changes.

Warning: This destroys all data. Never use --fresh in production.

# Reset everything and re-seed
grit migrate --fresh
grit seed

Adding New Models

When you run grit generate resource, it automatically:

  1. Creates a new Go model file
  2. Injects the model into AutoMigrate via the // grit:models marker
  3. Migrations run on next API startup or grit migrate

To add a model manually:

// apps/api/internal/models/post.go
type Post struct {
    ID        uint           `gorm:"primarykey" json:"id"`
    Title     string         `gorm:"size:255;not null" json:"title"`
    Content   string         `gorm:"type:text" json:"content"`
    Published bool           `gorm:"default:false" json:"published"`
    CreatedAt time.Time      `json:"created_at"`
    UpdatedAt time.Time      `json:"updated_at"`
    DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}

Then add it to AutoMigrate in models/user.go above the // grit:models marker.

Seeding

Running Seeders

grit seed

This creates:

AccountEmailPasswordRole
Adminadmin@example.comadmin123ADMIN
Jane Cooperjane@example.comadmin123EDITOR
Robert Foxrobert@example.comadmin123USER
Emily Davisemily@example.comadmin123USER
Michael Chenmichael@example.comadmin123USER (inactive)

How Seeders Work

Seeders live in apps/api/internal/database/seed.go. The main Seed function calls individual seeder functions:

func Seed(db *gorm.DB) error {
    if err := seedAdminUser(db); err != nil {
        return err
    }
    if err := seedDemoUsers(db); err != nil {
        return err
    }
    // grit:seeders  <-- add your own seeders above this marker
    return nil
}

Adding Custom Seeders

Add new seeder functions in seed.go:

func seedCategories(db *gorm.DB) error {
    categories := []models.Category{
        {Name: "Technology", Slug: "technology"},
        {Name: "Business", Slug: "business"},
        {Name: "Design", Slug: "design"},
    }

    for _, c := range categories {
        var count int64
        db.Model(&models.Category{}).Where("slug = ?", c.Slug).Count(&count)
        if count > 0 {
            continue
        }
        if err := db.Create(&c).Error; err != nil {
            return err
        }
    }

    return nil
}

Then call it from the Seed function:

if err := seedCategories(db); err != nil {
    return fmt.Errorf("seeding categories: %w", err)
}

Idempotent Seeders

Seeders check if records already exist before creating them. This means you can run grit seed multiple times without creating duplicates.

Common Workflow

# Initial setup
docker compose up -d          # Start PostgreSQL
grit migrate                  # Create tables
grit seed                     # Populate with test data

# After adding a new resource
grit generate resource Post --fields "title:string,content:text"
grit migrate                  # Apply new table

# Reset during development
grit migrate --fresh          # Drop everything
grit seed                     # Re-populate

On this page