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:
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!
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!
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
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
go generate
. But Runway currently does not automatically run go generate
for you.Enable go generate
on Runway:
runway app config set BP_GO_GENERATE=true
[build]
[[build.env]]
name="BP_GO_GENERATE"
value="true"
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.