Default Values in Vapor Fluent

My recent tinkering has been with Vapor, and while I mostly like their Fluent ORM, it has some rough edges and semi-undocumented behavior. At some point, I’ll feel confident enough in what I’ve learned through trial and error (combined with reading the source code – open source!) to actually make some contributions to the documentation, but for now, I’m going to throw some of the things I struggled with up here.

If you’re using a migration to add a column, and specifically want it to be non-null, you’ll need a default value. My first approach was to do a three-step migration, adding the column as nullable, then filling the default value on all rows, and then setting the column to be non-null, but that didn’t feel right. Eventually, though, I figured out how to express a DEFAULT constraint in Fluent:

let defaultValueConstraint = SQLColumnConstraintAlgorithm.default(/* your default value here */)

Then, in your actual schema builder call:

.field("column_name", /* your type */, .sql(defaultValueConstraint), .required)

Note that SQLColumnConstraintAlgorithm isn’t available from the Fluent module, you’ll need to import SQLKit first.

And here, a full worked example:

import Vapor
import Fluent
import SQLKit

struct DemoMigration: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
let defaultValueConstraint = SQLColumnConstraintAlgorithm.default(false)
return database.schema(DemoModel.schema)
.field(DemoModel.FieldKeys.hasBeenTouched, .bool, .sql(defaultValueConstraint), .required)
.update()
}

func revert(on database: Database) -> EventLoopFuture<Void> {
database.schema(DemoModel.schema)
.deleteField(DemoModel.FieldKeys.hasBeenTouched)
.update()
}
}

(For context, I’m in the habit of having a static var schema: String { "demo_model" } and a struct FieldKeys { static var hasBeenTouched: FieldKey { "has_been_touched" } } within each of my Fluent models – it keeps everything nice and organized, and avoids having stringly-typed issues all over the place.)