Skip to content

Handy JSON response #50

Description

@gotev

JSON responses are common for a web server. With this extension (serializing dates as millis by default to ease JS clients):

extension HTTPResponse {
    static let jsonTimestampMillisEncoder: JSONEncoder = {
        let jsonEncoder = JSONEncoder()
        jsonEncoder.dateEncodingStrategy = .millisecondsSince1970

        return jsonEncoder
    }()

    static func json(_ encodable: Encodable, jsonEncoder: JSONEncoder = jsonTimestampMillisEncoder) throws -> HTTPResponse {
        HTTPResponse(statusCode: .ok, body: try jsonEncoder.encode(encodable))
    }
}

one can easily return a JSON ready to be consumed by another application:

struct Cat: Codable {
    let birthDate: Date
    let name: String
}

struct MyHandler : HTTPHandler {
    func handleRequest(_ request: HTTPRequest) async throws -> HTTPResponse {
        try .json(Cat(birthDate: Date(), name: "Loris"))
    }
}

If you think it's a valuable addition, feel free to add it in the library 🍻

Activity

  1. swhitty commented on Mar 5, 2023

    @swhitty
    Owner

    Thank you for the suggestion 🙏🏻.

    I also use extensions similar to these in my own projects. As you have illustrated the difficulty is in choosing appropriate defaults for things like Date encoding and the status code to return.

  2. swhitty commented on Oct 29, 2023

    @swhitty
    Owner

    Hi @gotev I've been thinking a lot about your suggestion think there is a nice declarative solution SE-0389 Attached Macros available in Swift 5.9 and later.

    I'm interested in your feedback, the implementation is currently available on the branch preview/macro where handlers can annotate functions with routes:

    @HTTPHandler
    struct MyHandler {
    
      static let jsonTimestampMillisEncoder: JSONEncoder = {
        let jsonEncoder = JSONEncoder()
        jsonEncoder.dateEncodingStrategy = .millisecondsSince1970
        return jsonEncoder
      }()
    
      @JSONRoute("/cat", encoder: Self.jsonTimestampMillisEncoder)
      func getCat() -> Cat {
        Cat(birthDate: Date(), name: "Loris")
      }
    }
    
    let server = HTTPServer(port: 80, handler: MyHandler())
    try await server.start()

    The macro synthesises conformance to HTTPHandler delegating handling to the first matching route.
    Read more here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions