Runway
Runway is built and operated in the EU!

Migrations

Running migrations on your database is an important part of the lifecycle of an application. Typically, you start off with a structure and it evolves over time. Think of the following examples for a migration:

Lifecycle

Create migration

Part of the joy of database is of course writing migrations (irony). There are usually two options — one of them is using an ORM to maintain the schema in code and that typically comes with tooling to generate a table.

The second option is writing your migrations from scratch — not our choice, but to each their own!

Deployment

Another important part is planning the deployment, or the rollout: the best option is to darklaunch database changes — run a migration before your application code needs it; in form of multiple deployments.

But aside from darklaunching, or doing it just in time — how do we get the migrations to run? With (spoiler!) entgo. There’s tooling involved, so let’s dive into the example!

Practical Example

In order to keep this guide somewhat contained, we’ll do the super unusual thing in Golang and use an ORM. And in this case we’ll use entgo!

To get started, install their code generation tool:

go get entgo.io/ent/cmd/ent

Schema

And initialize a new schema:

go run -mod=mod entgo.io/ent/cmd/ent new User

The above creates a ent/schema/user.go — feel free to edit it, by adding some fields:

// Fields of the User.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name").Default("Unknown"),
        field.String("city").Default("Berlin"),
    }
}

Verify the changes:

go run -mod=mod entgo.io/ent/cmd/ent describe ./ent/schema

You can generate the code needed for your app:

go generate ./ent

Enable go generate on Runway:

Runway CLI
runway app config set BP_GO_GENERATE=true
project.toml
[build]
  [[build.env]]
    name="BP_GO_GENERATE"
    value="true"

Rollout

For the rollout, let’s demonstrate the three options — automatically when the application boots, and the two that are more on demand.

Automatic migrations with entgo are super simple:

if err := client.Schema.Create(ctx); err != nil {
    log.Fatalf("failed creating schema resources: %v", err)
}

The code assumes you have successfully created the client (see database) and runs whenever your application starts.

The other option is to create a separate tool (or command) that applies the migrations for us. So instead of sticking this code into the main() of your application, you add another e.g. cmd/migrator/main.go and build it along the application.

See the monorepo guide for examples, and run it with:

runway app exec migrator

The third option includes creating Procfile for your application:

web: server
init: migrator

init will run the migrator tool when the application starts. The application will only start when init completed successfully.

To learn more about the Procfile, see the Procfile guide.