Let's get started with handling migrations in Go with GORM and Goose.
Prerequisites
- Go installed
- GORM Supported SQL Database(I'll be using
Postgresql
)
Note:
You may refer to this repo for the file structure.
Install Goose Locally
Follow the steps here to install Goose for your operating system.
I tried using the installation script with Ubuntu 20.04
but I run into errors and found a solution here
wget http://github.com/golang-migrate/migrate/releases/latest/download/migrate.linux-amd64.deb
sudo dpkg -i migrate.linux-amd64.deb
goose --help // To test if install went well.
Start Dummy Project
- Create a directory
mkdir app
cd app
- Initialize it as a Go project
go mod init app
- Create
main.go
in the root of your project
package main
func main() {
}
Getting Started With GORM
- Create database directory
mkdir database
cd database
touch postgres.go
- Add the following to
database/postgres.go
package database
import (
"database/sql"
"os"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
var dbURL := os.Getenv("GOOSE_DBSTRING")
var GORM_DB *gorm.DB
var SQL_DB *sql.DB
var DB_MIGRATOR gorm.Migrator
func ConnectToDatabase() (error) {
db, err := gorm.Open(postgres.Open(dbURL), &gorm.Config{})
if err == nil {
GORM_DB = db
SQL_DB, _ = db.DB()
DB_MIGRATOR = db.Migrator()
}
return err
}
- At this point you'll have failed import errors, so let's fix that
go mod tidy
- Now create
env
in your projects root directory
mkdir env
touch env/goose.env
- Add the following to
env/goose.env
export GOOSE_DRIVER='postgres'
export GOOSE_DBSTRING='postgres://user:password@localhost:5432/db_name?sslmode=disable'
export GOOSE_MIGRATION_DIR='./migrations'
- Now source the environment variables
source env/goose.env
Creating Models and Migrations
- Create a models directory in your projects root directory
mkdir models
touch models/user.go
- Add the following to
models/user.go
package models
import (
"gorm.io/gorm"
)
type User struct {
*gorm.Model
Name string
Email string
Password string
}
- Now run goose command to create migration file
mkdir migrations
goose -s create create_user
- After this, you'll have import errors. Let's fix this
go mod tidy
- Update the
00001_create_user.go
file to have the following
package migrations
import (
"app/database"
"app/models"
"context"
"database/sql"
"github.com/pressly/goose/v3"
)
func init() {
goose.AddMigrationContext(upCreateUser, downCreateUser)
}
func upCreateUser(ctx context.Context, tx *sql.Tx) error {
return database.DB_MIGRATOR.CreateTable(&models.User{})
}
func downCreateUser(ctx context.Context, tx *sql.Tx) error {
return database.DB_MIGRATOR.DropTable(&models.User{})
}
Bringing It All Together
- Update the
main.go
file to have the following
package main
import (
"app/database"
_ "app/migrations" // This loads the migrations init functions
"github.com/pressly/goose/v3"
)
func main() {
err := database.ConnectToDatabase()
if err != nil {
panic(err)
}
if err := goose.SetDialect("postgres"); err != nil {
panic(err)
}
if err := goose.Up(database.SQL_DB, "migrations"); err != nil {
panic(err)
}
// Start server...
}
- Now you can run migrations before starting the server
go run .