Database Migrations With Vapor
In this Server-Side Swift tutorial, learn how to perform database migrations with Vapor on your application database – a useful tool for tasks such as creating tables, seeding data, and adding columns. By Heidi Hermann.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Database Migrations With Vapor
25 mins
- Getting Started
- Configuring Your Local PostgreSQL Database Using Docker
- Connecting to Your Database
- Writing Your First Migration
- Migrations in Vapor 4
- Creating a Tools Migration
- Running Your Application
- Implementing FieldKeys
- Rewriting Your First Migration Using FieldKeys
- Replacing Stringly Typed Keys with FieldKeys in Tool and Its Migration
- Reverting Your Migration
- Rerunning Your First Migration
- Adding a Maker to Your Tools
- Adding a New Migration
- Running Your Migration
- Adding a Unique Requirement to Your Tool/Maker Combination
- Building and Running Your App
- Seeding Data Using Migrations
- Where to Go From Here?
Creating a Tools Migration
In Xcode, add a new folder named Migrations inside your App folder.
Next, add a new Swift file named 21-05-31_Tool+Create.swift to hold your migration defining the entity's table.
For example, you could use 20210123_Tool+AddUpdatedAt if you needed to add an updated_at
field to an existing table for the Tool
model.
- Prefix the file name with the date it was created in year/month/day format. This allows you to maintain an overview of the proper order your migrations were created in and the order in which they should be executed.
- Use a descriptive name for the migration explaining what the migration is doing to the model.
- Prefix the file name with the date it was created in year/month/day format. This allows you to maintain an overview of the proper order your migrations were created in and the order in which they should be executed.
- Use a descriptive name for the migration explaining what the migration is doing to the model.
For example, you could use 20210123_Tool+AddUpdatedAt if you needed to add an updated_at
field to an existing table for the Tool
model.
Replace the new file's contents with:
// 1
import FluentKit
// 2
extension Tool {
struct Create: Migration {
// 3
func prepare(on database: Database) -> EventLoopFuture<Void> {
return database
.schema("tools") // 4
.id() // 5
.field("name", .string, .required) // 6
.field("created_at", .datetime) // 7
.field("updated_at", .datetime)
.create() // 8
}
// 9
func revert(on database: Database) -> EventLoopFuture<Void> {
return database.schema("tools").delete()
}
}
}
Here you:
- Import
FluentKit
to exposeMigration
. - Extend your
Tool
database model and create astruct
called Create and make it conform to theMigration
protocol. The migration is a nested type to keep migrations organized with their model. - Add the required method
func prepare(on:) -> EventLoopFuture
. - Provide the schema of the database model.
- Create the
id
for the model. - Add a field for the name of the Tool.
- Create the two timestamps for Tool —
createdAt
andupdatedAt
. - Create the table.
- Add the required method,
func revert(on:) -> EventLoopFuture
. Inside, call the.delete()
method on the Tool table.
- The field names are in snake_case, even though the properties in the application are in camelCase. This is a convention from PostgreSQL, since, by default, it's case insensitive in regard to keys.
- The
.id()
builder method is only available if your ID type isUUID
. If you're using anint
, you'll have to define the property yourself using.field("id", .int, .identifier(auto: true))
.
- The field names are in snake_case, even though the properties in the application are in camelCase. This is a convention from PostgreSQL, since, by default, it's case insensitive in regard to keys.
- The
.id()
builder method is only available if your ID type isUUID
. If you're using anint
, you'll have to define the property yourself using.field("id", .int, .identifier(auto: true))
.
Next, open /Configurations/configure.swift and register your migration with the application by replacing the comment on line 47 with the following:
app.migrations.add(Tool.Create())
Next time you run your application with migrate
enabled, your model will be created in your database.
Running Your Application
Now, open your build scheme and add migrate
under Arguments Passed On Launch.
Then, run your application.
This will prompt a dialog in your output window telling you which migrations will be run and asking you if you're sure you want to go ahead with them.
Type y and press enter.
Wait for the success command to print, along with a message that the command ended. Then, open the build schema and remove the migrate argument.
You can explicitly run them from your terminal using vapor run migrate
, or from inside Xcode like you just did. In both cases, you'll receive the command prompt, but you can also pass -y
as an argument to skip the prompt.
You can also run the migrations automatically by either adding try app.autoMigrate().wait()
inside your configure.swift, or passing the --auto-migrate
flag when you run your application.
There are a number of options for you to run your migrations.
You can explicitly run them from your terminal using vapor run migrate
, or from inside Xcode like you just did. In both cases, you'll receive the command prompt, but you can also pass -y
as an argument to skip the prompt.
You can also run the migrations automatically by either adding try app.autoMigrate().wait()
inside your configure.swift, or passing the --auto-migrate
flag when you run your application.
In the next section, you'll view your database.
Viewing Your Database With Postico
Now, access your database with Postico.
You can see that Tool/code> has been added to the database, along with another table named _fluent_migrations.
First, open _fluent_migrations.
Vapor autogenerates this table and holds a registry of all the migrations you've run.
Each migration has a unique identifier, the name you gave it, a batch number and timestamps for creation and update.
Since it was your first migration, the batch number is 1. Each time you run a new migration, the batch number will increase by one.
Next, open the tools table, and in the bottom-left corner, choose to view the Structure.
Here, you see that the table has four columns that match the ones you defined in your migration:
-
id: Of type
UUID
, and with the primary key constraint enabled. -
name: Of type
TEXT
, and withNOT NULL
enabled. -
created_at: Of type
TIMESTAMPZ
(timestamp with time zone). -
updated_at: Of type
TIMESTAMPZ
.
Moving on, you'll learn about FieldKey
s.