Search code examples
swiftpostgresqlfluentvaporserver-side-swift

Understanding Migrations in Vapor-Fluent (Server side Swift)


I'm writing a web service in Swift using Vapor framework.

I have model named Item. Intially it has only name and id properties.

typealias VaporModel = Content & PostgreSQLModel & Parameter
final class Item: VaporModel {
    var id: Int?
    var name: String
}

After I configure a controller for the model and add the routes, when I hit the post Item request, I get the error as Model.defaultDatabase is required to use as DatabaseConnectable. I think the error is because I have not added Item to Migrations in configure.swift and I do the same after conforming Item to PostgreSQLMigration.

var migrations = MigrationConfig()
migrations.add(model: Item.self, database: .psql)
services.register(migrations)

Now, I am able to hit the post request and create items in the database.

So I understand that Migration protocol creates the default schema for a model and adds a new table to the database with the model's properties as columns.

Now I want to add a property such as price to my Item class. Now when I hit the post request, I get the error as column "price" of relation "Item" does not exist. I assume the Migration protocol will be able to identify the schema changes and the column to my table (that's what I was used to in while using Realm for my iOS apps). But I am wrong and I read through the Migration docs and implement the prepare and revert methods in migration like below.

extension Item: PostgreSQLMigration {
    static func prepare(on conn: PostgreSQLConnection) -> Future<Void> {
        return Database.create(self, on: conn) { creator in
            creator.field(for: \.price)
        }
    }

    static func revert(on connection: PostgreSQLConnection) -> EventLoopFuture<Void> {
        return Future.map(on: connection) { }
    }
} 

I'm still struck with the same error column "price" of relation "Item" does not exist. What am I missing here? Is my migration code correct?

Also, I understand that if am not making any changes to the Model, I can comment out the migration config, because they need not run every time I run the service. Is that correct?


Solution

  • With your code you haven't added a new migration. You have implemented a manual initial migration, but the initial migration has run already as requested (migrations.add(model: Item.self, database: .psql). To create a new migration you would need sth like:

    struct ItemAddPriceMigration: Migration {
        typealias Database = PostgreSQLDatabase
    
        static func prepare(on conn: PostgreSQLConnection) -> EventLoopFuture<Void> {
    
            return Database.update(Item.self, on: conn) { builder in
                builder.field(for: \.price)
            }
        }
    
        static func revert(on conn: PostgreSQLConnection) -> EventLoopFuture<Void> {
            return conn.future()
        }
    }
    

    And then you need to add it in configure:

    migrations.add(migration: ItemAddPriceMigration.self, database: .psql)