Search code examples
postgresqlherokuheroku-postgresvapor

How do I convert Postgres dates to ISO8601 in JSON responses with Vapor 3 running on Heroku?


I have a Vapor 3 API up on Heroku. Unfortunately, it's not handling dates correctly. Originally, I thought I could just treat dates like strings for simplicity in Vapor, like so:

struct MyModel {
  var time: String?
}

But whenever I fetch MyModels from the db and return it, the time key doesn't appear at all (while other keys and values have no problems). I thought I might be able to just change time's type to Date, but that resulted in the same thing, and I've already used ContentConfig to set the JsonEncoder.dateEncodingStrategy to .iso8601 (again, no luck – perhaps because dateEncodingStrategy only supports millis on Linux, which is what Heroku uses?).

How do I convert Postgres dates to ISO8601 in json with Vapor 3 running on Heroku?


Solution

  • Got it working! Just changed the properties to Dates, and manually converted request query parameters to Dates as well (for use in filter calls). So, a little more by hand than most things in Vapor 3, but not terrible.

    Eg my model looks like this now:

    struct MyModel {
      var time: Date?
    }
    

    And then when I try to filter by date I do something like this:

    var builder = MyModel.query(on: req)
    if let afterString: String = try? self.query.get(String.self, at: "after") {
        let afterDate: Date? = DateFormatter.iso8601Full.date(from: afterString)
        builder = builder.filter(\.time > afterDate)
    }
    

    where after is a url parameter, and DateFormatter.iso8601Full is my iso8601 date formatter. Then when I'm returning an array of MyModels in a response, I map the array to an array of MyModelResponseObjects which look like this:

    struct MyModelResponseObject {
      var time: String?
    }
    

    by doing something like this:

    myModelsFuture.all().map(to: [MyModelResponseObject].self, { (myModels) -> [MyModelResponseObject] in 
        return myModels.map { it in
            return MyModelResponseObject(time: DateFormatter.iso8601Full.string(from: it.time ?? Date.init(timeIntervalSince1970: 0)))
        }
    }
    

    So basically I'm manually converting the dates into the format that I want when returning them in JSON.