Vapor

Vapor is one of two swift web frameworks to have gained traction (the other is Kitura). Both have their rough edges and reasons to use or not use them. I have slightly more experience with Vapor. Though Kitura feels more native-swift.

Specify the http status error

To the best of my knowledge, there are two easy ways to return a custom http status error: throw an AbortError, or return a Response. (The other way is to create your own type that conforms to ResponseDecodable, and have it set the http status in encode(status:headers:for:))

AbortError

AbortError is a protocol, which means you have to create your own instance of it in order to return one. Simple enough, but still annoying. Your custom implementation needs to have 3 properties: status, reason, and identifier. As the name indicates, you throw your error from the request handler.

Return a Response

From your asynchronous request handler, you can chain on .encode(status:for:) to set the status. (The second parameter is the request object your request handler was called with).

Testability

I haven’t gotten around to writing a microframework to do this, but here’s my Application extension I add to every vapor project I do:

import Vapor

@testable import App

extension Application {
    static func testable() throws -> Application {
        var config = Config.default()
        var services = Services.default()
        var env = Environment.testing
        try App.configure(&config, &env, &services)
        let app = try Application(config: config, environment: env, services: services)
        try App.boot(app)

        return app
    }

    func sendRequest<Body>(to path: String, method: HTTPMethod, headers: HTTPHeaders = .init(), body: Body?) throws -> Response where Body: Content {
        let httpRequest = HTTPRequest(method: method, url: URL(string: path)!, headers: headers)
        let wrappedRequest = Request(http: httpRequest, using: self)
        if let body = body {
            try wrappedRequest.content.encode(body)
        }
        let responder = try make(Responder.self)

        return try responder.respond(to: wrappedRequest).wait()
    }
}

This is used as:

let subject = try Application.testable()

let response = try subject.sendRequest(to: "/my/path", method: .PUT, body: Optional<String>.none)

Last updated: 2021-05-13 15:18:56 -0700