Search code examples
swiftvaporvapor-fluent

How do I add an additional property to a Model?


I have a Model which conforms to Content:

import Vapor
import Fluent

final class User: Model, Content {
    static let schema = "user"
    
    @ID(key: .id)
    var id: UUID?

    @Field(key: "email")
    var email: String

    init() { }

    init(id: UUID? = nil, email: String) {
        self.id = id
        self.email = email
    }
}

This can be fetched from the database and directly returned from a route, so the client receives the users as JSON.

Now I want to add an additional property to the user, which isn't stored in the database but a fixed value or just added after the user is loaded from the database:

final class User: Model, Content {
    // ...
    var someOther: String = "hello"
    // ...
}

or

final class User: Model, Content {
    // ...
    var someOther: String? // set later
    // ...
}

Now the problem is that this property is never added to the JSON, so the client only gets the user's id and email and NOT the someProperty. How do I fix this?


Solution

  • When you want to send or receive data from your client that has a different representation from what your model has, it's common practice to define a custom type that you can then convert to and from the model type.

    Given this, you would define a new User.Response type that conforms to Content instead of the User model, and return that from your route instead.

    extension User {
        struct Response: Content {
            let id: UUID
            let email: String
            let otherValue = "Hello World"
        }
    }
    
    
    func user(request: Request) throws -> EventLoopFuture<User.Response> {
        return User.find(UUID(), on: request.db).flatMapThrowing { user in
            guard let id = user.id else {
               throw Abort(.internalServerError, "User is missing ID")
            }
    
            return User.Response(id: id, email: user.email) 
        }
    }
    

    The reason the additional value that you added wasn't encoded from your model is because the default encoder for model types iterates over the field properties and encodes those properties, so if you have a property that doesn't use one of the ID, Field, Parent, Children, or other field property wrappers, it won't be encoded.