How to customise date format when using `application/x-www-form-urlencoded` encoded request?


I would like to customise the expected format of dates (for example "yyyy-MM-dd") that I will be receiving from the form on my webpage (encoded as application/x-www-form-urlencoded, not JSON).

Any recommendation on how to do it nicely?


You can override the content decoders as per the docs Vapor: Basics → Content

So you can create a new URLEncodedFormDecoder with a custom callback and then set that

If I am to override the date parsing part, can I subclass the existing one or do I have to write (I.e. copy paste) the full one from scratch?

I didn’t have the chance to check how the existing URLEncodedFormDecoder is implemented.

Also, if I am to go down this route, does it mean I have to change the global parser or otherwise I will loose the inbuilt content type negotiation logic? Is this correct?

I found this URLEncodedFormDecoder constructor:

public init(configuration: Configuration = .init()) { }

I did not see it, and it answers my question regarding how to override the date parsing logic.

Correct the type is public so you can initialise it how you like. In terms of changing the global parser etc, you can either override the global decoder for .urlEncodedFormData or you can use it as a one off for a specific route

In the end, I decided to go fancier and create a custom Codable type like this:

import Foundation
import Vapor

struct DateOnly {
    let date: Date

extension DateOnly: Codable {
    init(from decoder: any Decoder) throws {
        var container = try decoder.unkeyedContainer()
        let value = try container.decode(String.self)
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withFullDate]

        guard let value = value) else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unexpected date pattern")

        date = value

    func encode(to encoder: any Encoder) throws {
        var container = encoder.unkeyedContainer()
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withFullDate]
        try container.encode(formatter.string(from: date))

extension DateOnly: Content {}

It will encode into and decode from a simple string, and I use it whenever I expect an ISO 08601 date only, regardless of the content type.