2

I have a vapor 4 application. I do a query from database for getting some items and I want to perform some manual calculation based on the returned values before finishing the request. here a sample code of what I am trying to achieve.

final class Todo: Model, Content {

    static var schema: String = "todos"

    @ID(custom: .id)
    var id: Int?

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

    var someValue: Int?

}

/// Allows `Todo` to be used as a dynamic migration.
struct CreateTodo: Migration {
    func prepare(on database: Database) -> EventLoopFuture<Void> {
        database.schema(Todo.schema)
            .field("id", .int, .identifier(auto: true))
            .field("title", .string, .required)
            .create()
    }

    func revert(on database: Database) -> EventLoopFuture<Void> {
        database.schema(Todo.schema).delete()
    }
}
final class TodoController:RouteCollection{

    func boot(routes: RoutesBuilder) throws {

        routes.get("tmp", use: temp)
    }

    func temp(_ req:Request) throws -> EventLoopFuture<[Todo]> {

        Todo.query(on: req.db).all().map { todos in

            todos.map {
                $0.someValue = (0...10).randomElement()!
                return $0
            }
        }
    }

}

The problem is that those manual changes, aren't available in response. In this case someValue property.

Thanks.

[
    {
        "title": "item 1",
        "id": 1
    },
    {
        "title": "item 2",
        "id": 2
    }
]
marc-medley
  • 8,931
  • 5
  • 60
  • 66

1 Answers1

4

The problem you're hitting is that Models override the Codable implementations. This allows you to do things like passing around parents and not adding children etc.

However, this breaks your case. What you should do, is create a new type if you want to return a Todo with another field that isn't stored in the database, something like:

struct TodoResponse: Content {
  let id: Int
  let title: String
  let someValue: Int
}

And then convert from your database type to your response type in your route handler (this is a pretty common pattern and the recommended way to do it in Vapor)

0xTim
  • 5,146
  • 11
  • 23
  • I'm trying to do something similar, but specific to integration with a Leaf template rather than in the API context. In this case, I'm a little fuzzy on the correct/swift-y approach here. The struct you're recommending here would belong to the Model or the ModelController or the WebController ? – Erik Ableson Jan 05 '23 at 13:41
  • I'd just put it in a folder called `LeafModels` – 0xTim Jan 05 '23 at 20:21