Episode #357

Setting up a Database with Fluent

Series: Server-side Swift with Vapor

19 minutes
Published on September 28, 2018

This video is only available to subscribers. Get access to this video and 573 others.

Most server applications will need to store some data in a database. For Vapor applications, this is done with Fluent, a Swift Object-Relational-Mapper for persisting objects to a database. Fluent supports SQLite, Postgres, and Mysql. In this episode we will learn how to set up Fluent with a SQLite database for development. We'll create our first model object, and discuss how Fluent supports migrations for evolving the database schema over time.

Episode Links

Creating a Bare Vapor Project

We are going to start by creating a project that has nothing in it. This will help us understand how to set up a dependency like Fluent.

$ vapor new blog --template=https://github.com/twostraws/vapor-clean.git

Once that is created, start the initial build.

$ vapor build

Adding FluentSQLite with Swift Package Manager

We're going to use SQLite as our database for this demo. Let's open up Package.swift and add our dependency:

// swift-tools-version:4.0

import PackageDescription

let package = Package(
    name: "blog",
    dependencies: [
        .package(url: "https://github.com/vapor/vapor.git", from: "3.1.0"),
        .package(url: "https://github.com/vapor/fluent-sqlite.git", from: "3.0.0")
    ],

    targets: [
        .target(name: "App", dependencies: ["Vapor", "FluentSQLite"]),
        .target(name: "Run", dependencies: ["App"]),
        .testTarget(name: "AppTests", dependencies: ["App"]),
    ]
)

We can build again to fetch this new dependency and then generate an Xcode project:

$ vapor build
$ vapor xcode

Configuring the Database

We need to configure our application to use the database. Open up configure.swift.

import Vapor
import FluentSQLite

public func configure(
    _ config: inout Config,
    _ env: inout Environment,
    _ services: inout Services
) throws {

    // Other setup ...

    try services.register(FluentSQLiteProvider())
    let db = try SQLiteDatabase(storage: .file(path: "db.sqlite"))

    var dbConfig = DatabasesConfig()
    dbConfig.add(database: db, as: .sqlite)
    services.register(dbConfig)
}

This will create our database as a file, located in the built products directory. You can see this path output in the console when you run the application in case you want to inspect the database manually.

Creating a Model

All Fluent models must implement the Model protocol. This provides information to the system about what type of primary key the model has, what the name of the table is, and whether it supports automatic timestamps like createdAt and updatedAt.

In most cases, you can use the Model sub-protocol defined by the provider.

import Vapor
import FluentSQLite

final class Post : SQLiteModel {
    var id: Int?
    var title: String
    var body: String
    var author: String
    var publishedAt: Date?

    static let entity = "posts"

    init(title: String, body: String, author: String) {
        self.title = title
        self.body = body
        self.author = author
    }
}

Configuring Migrations

To get our posts table to be created, we need to make our model adopt the Migration protocol:

extension Post : Migration { }

Then we need to configure migrations to run on application startup. In configure.swift, after the database has been configured:

var migrations = MigrationConfig()
migrations.add(model: Post.self, database: .sqlite)
services.register(migrations)

We'll get the first migration for free, but as we alter this table and add new fields, we'll have to create migrations to update our database as well.

This episode uses Vapor 3.0.0, Xcode 10.0, Fluentsqlite 3.0.0, Swift 4.2.