Use Atlas + GORM for database migrations

Atlas is a popular tool for managing database migrations. GORM is a popular ORM for Go.

Encore provides excellent support for using them together to easily manage database schemas and migrations. Encore executes database migrations using golang-migrate, which Atlas supports out-of-the-box. This means that you can use Atlas to manage your Encore database migrations.

The easiest way to use Atlas + GORM together is with Atlas's support for external schemas.

Setting up GORM

To set up your Encore application with GORM, start by installing the GORM package and associated Postgres driver:

go get -u

Then, in the service that you want to use GORM for, add the *gorm.DB as a dependency in your service struct (create a service struct if you don't already have one).

For example, if you had a service called blog:

package blog import ( "" "" "" ) //encore:service type Service struct { db *gorm.DB } var blogDB = sqldb.NewDatabase("blog", sqldb.DatabaseConfig{ Migrations: "./migrations", }) // initService initializes the site service. // It is automatically called by Encore on service startup. func initService() (*Service, error) { db, err := gorm.Open(postgres.New(postgres.Config{ Conn: blogDB.Stdlib(), })) if err != nil { return nil, err } return &Service{db: db}, nil }

Finally, create the migrations directory inside the blog directory if it doesn't already exist. This is where Atlas will put your database migrations.

Setting up Atlas

First install Atlas.

Then, add an atlas.hcl file inside the blog directory:

data "external_schema" "gorm" { program = ["encore", "alpha", "exec", "./scripts/atlas-gorm-loader"] } env "local" { src = data.external_schema.gorm.url dev = "docker://postgres/15" migration { dir = "file://migrations" format = golang-migrate } format { migrate { diff = "{{ sql . \" \" }}" } } }

Finally we need to create the atlas-gorm-loader script referenced above. We'll use Atlas's provided atlas-provider-gorm library.

package main import ( "fmt" "io" "os" _ "" "" "" ) // Define the models to generate migrations for. var models = []any{ &blog.Post{}, &blog.Comment{}, } func main() { stmts, err := gormschema.New("postgres").Load(models...) if err != nil { fmt.Fprintf(os.Stderr, "failed to load gorm schema: %v\n", err) os.Exit(1) } io.WriteString(os.Stdout, stmts) }

Creating migrations

Then, whenever you're ready to generate a migration, run:

$ atlas migrate diff --env local <name-of-migration>

This will generate a new migration file in the migrations directory, which will be automatically applied when running encore run.