Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vapor: Date object is decoded in a string format, but the decoder expects a double

Tags:

swift

vapor

I have a data model that looks like this:

struct Post: Content, MySQLModel, Migration, Equatable {
    var id: Int?
    var userId: Int
    var title: String
    var body: String
    var creationDate: Date?
    var lastEditDate: Date?

    static func prepare(on connection: MySQLConnection) -> Future<Void> {
        return MySQLDatabase.create(self, on: connection) { builder in
            builder.field(for: \.id, isIdentifier: true)
            builder.field(for: \.userId)
            builder.field(for: \.title)
            builder.field(for: \.body, type: .text())
            builder.field(for: \.creationDate)
            builder.field(for: \.lastEditDate)
        }
    }
}

And if I have some instances in the database, I can safely make a query passing the id of the post in the path, and I would get an object with a creation/last edit date formatted in a string format:

func retrievePost(on req: Request) throws -> Future<Post> {
    let id = try req.parameters.next(Int.self)

    return Post.find(id, on: req).map(to: Post.self) { post in
        guard let post = post else {
            throw Abort(.notFound)
        }

        return post
    }
}

If I make a GET query, this is what I get back in the response body:

{
    "body": "value",
    "id": 8723,
    "title": "value",
    "creationDate": "2020-05-27T15:24:41Z",
    "userId": 0
}

And this is my PUT method implementation:

func updatePost(on req: Request) throws -> Future<Post> {
    var updatedPost = try req.content.syncDecode(Post.self)

    guard let id = updatedPost.id else {
        throw Abort(.badRequest)
    }

    return Post.find(id, on: req).flatMap { post in
        guard let _ = post else {
            throw Abort(.notFound)
        }

        return updatedPost.save(on: req)
    }
}

But if I send a PUT request passing the same exact fields that I got in the GET response body, with the creation date formatted as a string, I get this error:

{
    "error": true,
    "reason": "Could not convert to `Double`: str(\"2020-05-27T15:24:41Z\")"
}

It's expecting a double. If I try sending the number of seconds after 1970 it works, but I don't understand why the same date object is encoded using a string date and decoded using a double. How to go around this problem?

like image 948
Ramy Al Zuhouri Avatar asked Sep 10 '25 15:09

Ramy Al Zuhouri


1 Answers

Using Vapor 4, I specified a custom JSONDecoder. This can be done globally, or as shown here for a single request.

In this scenario, I was decoding a POST'ed struct GetLogsRequest that included a date field.

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let request = try req.content.decode(GetLogsRequest.self, using: decoder)

See https://docs.vapor.codes/4.0/content/

like image 162
Adam Avatar answered Sep 13 '25 07:09

Adam