Using Fluent and Persisting Models in Vapor
The Fluent ORM lets you use any number of database engines in your Vapor app. Learn how to persist your models in your server side Swift apps using Vapor! By Tim Condon.
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
Using Fluent and Persisting Models in Vapor
15 mins
Vapor 4’s impressive advancements to Fluent make the entire database layer more cultivated. Fluent is Vapor’s ORM, or object relational mapping tool. It’s an abstraction layer between the Vapor app and the database that makes working with databases easier and includes several benefits.
With Fluent you don’t have to use the database directly! When you interact directly with a database, you write database queries as strings. These aren’t type-safe and can be painful to use from Swift.
Fluent lets you use any of several database engines, even in the same app. Even better, you don’t need to know how to write queries since you can interact with your models in a Swifty way.
Models, the Swift representation of your data, are used throughout Fluent. They’re the objects, such as user profiles, you save and access in your database. Fluent returns and uses type-safe models when interacting with the database which gives you compile-time safety.
In this tutorial, you’ll learn:
- How to configure a database for Vapor to use.
- How to use migrations to create tables in your database.
- How to use Fluent to save data in Vapor apps.
Getting Started
Use the Vapor Toolbox to create a new project. In Terminal, enter the following command:
cd ~/vapor
This command takes you into a directory called vapor inside your home directory and assumes you completed the steps in the “Getting Started with Server-side Swift using Vapor 4” tutorial.
Next, enter:
vapor new TILApp
When asked if you want to use Fluent, enter y and then press Enter. Next, enter 1 to choose PostgreSQL as the database, followed by Enter. This uses the template to create a new Vapor project called TILApp and configures PostgreSQL as the database.
The template provides example files for models, migrations and controllers. Since you’ll build your own, delete the examples. In Terminal, enter:
cd TILApp
rm -rf Sources/App/Models/*
rm -rf Sources/App/Migrations/*
rm -rf Sources/App/Controllers/*
If prompted to confirm the deletions, enter y. Now, open the project in Xcode:
open Package.swift
This creates a Xcode project from your Swift package, using Xcode’s support for Swift Package Manager. It takes a while to download all the dependencies for the first time. When it’s finished, you’ll see the dependencies in the sidebar and a TILApp scheme available:
First, open configure.swift in the App folder and delete the following line:
app.migrations.add(CreateTodo())
Next, open routes.swift and delete the following line:
try app.register(collection: TodoController())
You removed the remaining references to the template’s example model, migration and controller.
Next, you’ll create the model.
Creating the Model
Create a new Swift file in Sources/App/Models called Acronym.swift. Inside the new file, insert:
import Vapor
import Fluent
// 1
final class Acronym: Model {
// 2
static let schema = "acronyms"
// 3
@ID
var id: UUID?
// 4
@Field(key: "short")
var short: String
@Field(key: "long")
var long: String
// 5
init() {}
// 6
init(id: UUID? = nil, short: String, long: String) {
self.id = id
self.short = short
self.long = long
}
}
Here you:
- Define a class that conforms to
Model
. - Then specify the
schema
as required byModel
. This is the table’s name in the database. - Define an optional
id
property that stores the ID of the model, if one has been set. This is annotated with Fluent’s@ID
property wrapper which tells Fluent what to use to look up the model in the database. - Then define two
String
properties to hold the acronym and its definition. These use the@Field
property wrapper to denote a generic database field. Thekey
parameter is the name of the column in the database. - Provide an empty initializer as required by
Model
. Fluent uses this to initialize models returned from database queries. - Finally, provide an initializer to create the model as required.
If you’re coming from Fluent 3, this model looks very different. Fluent 4 leverages property wrappers to provide strong and complex database integration. @ID
marks a property as the ID for that table. Fluent uses this property wrapper to perform queries in the database when finding models.
It also uses the property wrapper for relationships. By default in Fluent, the ID must be an UUID
, or Universally Unique Identifier, and called id
.
@Field
marks the model’s property as a generic column in the database. Fluent uses the property wrapper to perform queries with filters. Using property wrappers lets Fluent update individual fields in a model, rather than the entire model.
You can also select specified fields from the database instead of all fields for a model. However, you should only use @Field
with non-optional properties. If you have an optional property in your model, you should use @OptionalField
.
Now you’ll create a table for the model.
Creating a Table for the Model
Before you can save the model in the database, you must create a table for it. Fluent does this with a migration.
Migrations let you make reliable, testable, reproducible changes to your database. Developers commonly use migrations to create a database schema, or table description, for models. They’re also used to seed data into your database or make changes to your models after you’ve created them.
Fluent 3 could infer a lot of the table information for you. However, this didn’t scale to large complex projects, especially when you needed to add or remove columns or even rename them.
In Xcode, create a new Swift file in Sources/App/Migrations called CreateAcronym.swift.
Insert the following in the new file:
import Fluent
// 1
struct CreateAcronym: Migration {
// 2
func prepare(on database: Database) -> EventLoopFuture<Void> {
// 3
database.schema("acronyms")
// 4
.id()
// 5
.field("short", .string, .required)
.field("long", .string, .required)
// 6
.create()
}
// 7
func revert(on database: Database) -> EventLoopFuture<Void> {
database.schema("acronyms").delete()
}
}
Here the code:
- Defines a new type,
CreateAcronym
, that conforms toMigration
. - Then implements
prepare(on:)
as required byMigration
. You call this method when you run your migrations. - Defines the table name for this model. This must match
schema
from the model. - Next, defines the ID column in the database.
- Defines columns for
short
andlong
. Sets the column type tostring
and marks the columns as required. This matches the non-optionalString
properties in the model. The field names must match the key of the property wrapper, not the name of the property itself. - Then creates the table in the database.
- Implements
revert(on:)
as required byMigration
. You call this function when you revert your migrations. This deletes the table referenced withschema(_:)
.
All references to column names and table names are strings because using properties causes issues if those property names change in the future.
Migrations only run once: Once they run in a database, they never execute again. So, Fluent won’t attempt to recreate a table if you change the migration.
Now that you have a migration for Acronym
, you can tell Fluent to create the table.