✎ Edit on GitHub

Model

Model is the base protocol for any of your application's models, especially those you want to persist.

Model is only available in Vapor, the Fluent equivalent is Entity

Example

Let's create a simple User model.

final class User {
    var name: String

    init(name: String) {
        self.name = name
    }
}

The first step to conforming to Model is to import Vapor and Fluent.

import Vapor
import Fluent

Then add the conformance to your class.

final class User: Model {
    ...
}

The compiler will inform you that some methods need to be implemented to conform.

ID

The first required property is an identifier. This property will contain the identifier when the model is fetched from the database. If it is nil, it will be set when the model is saved.

final class User: Model {
    var id: Node?
    ...
}

Node Initializable

The next requirement is a way to create the model from the persisted data. Model uses NodeInitializable to achieve this.

final class User: Model {
    init(node: Node, in context: Context) throws {
        id = try node.extract("id")
        name = try node.extract("name")
    }
    ...
}

The keys id and name are what we expect the columns or fields in the database to be named. The extract call is marked with a try because it will throw an error if the value is not present or is the wrong type.

Node Representable

Now that we have covered initializing the model, we need to show how to save it back into the database. Model uses NodeRepresentable to achieve this.

final class User: Model {
    func makeNode(context: Context) throws -> Node {
        return try Node(node: [
            "id": id,
            "name": name
        ])
    }
    ...
}

When a User is saved, the makeNode() method will be called and the resulting Node will be saved to the database. The keys id and name are what we expect the columns or fields in the database to be named.

In most of the cases you do not need to be concerned about context argument of the makeNode(context:) method. It’s a part of the protocol that allows extensibility in more advanced or specific scenarios.

Preparations

Some databases, like MySQL, need to be prepared for a new schema. In MySQL, this means creating a new table. Preparations are also equatable to migrations, as they can be used to alter schemas after they've already been created.

Prepare

Let's assume we are using a SQL database. To prepare the database for our User class, we need to create a table. If you are using a database like Mongo, you can leave this method unimplemented.

final class User {
    static func prepare(_ database: Database) throws {
        try database.create("users") { users in
            users.id()
            users.string("name")
        }
    }
    ...
}

Here we create a table named users that has an identifier field and a string field with the key name. This matches both our init(node: Node) and makeNode() -> Node methods.

Revert

An optional preparation reversion can be created. This will be run if vapor run prepare --revert is called.

final class User {
    static func revert(_ database: Database) throws {
        try database.delete("users")
    }
    ...
}

Here we are deleting the table named users.

Preparations as Migrations

If you want to add a field to your table after you've already created the initial schema, you can create a struct or class that conforms to Preparation like so:


struct AddFooToBar: Preparation {
    static func prepare(_ database: Database) throws {
        try database.modify("bars", closure: { bar in
            bar.string("foo", length: 150, optional: false, unique: false, default: nil)
        })
    }

    static func revert(_ database: Database) throws {

    }
}

Then, in your Droplet setup, add this line: drop.preparations.append(AddFooToBar.self)

Droplet

To run these prepations when the applications boots, you must add the Model to the Droplet.

let drop = Droplet()

drop.preparations.append(User.self)

Note: Preparations must be appended before the Droplet is run.

Full Model

This is what our final User model looks like:

import Vapor
import Fluent

final class User: Model {
    var id: Node?
    var name: String

    init(name: String) {
        self.name = name
    }

    init(node: Node, in context: Context) throws {
        id = try node.extract("id")
        name = try node.extract("name")
    }

    func makeNode(context: Context) throws -> Node {
        return try Node(node: [
            "id": id,
            "name": name
        ])
    }

    static func prepare(_ database: Database) throws {
        try database.create("users") { users in
            users.id()
            users.string("name")
        }
    }

    static func revert(_ database: Database) throws {
        try database.delete("users")
    }
}

Interacting

Now that User conforms to Model, it has a plethora of new methods like find(), query(), makeJSON() and more.

Fetch

Models can be fetched by their database identifier.

let user = try User.find(42)

Save

Newly created models can be saved to the database.

var user = User(name: "Vapor")
try user.save()
print(user.id) // prints the new id

Delete

Persisted models with identifiers can be deleted.

try user.delete()

Model vs. Entity

Model has a couple of extra conformances that a pure Fluent entity doesn't have.

public protocol Model: Entity, JSONRepresentable, StringInitializable, ResponseRepresentable {}

As can be seen in the protocol, Vapor models can automatically convert to JSON, Response, and even be used in type-safe routing.

Options

Change the table/collection name

static var entity = "new_name"