import Vapor extension Application { func getCatching(_ path: PathComponent..., call: @escaping (Request) throws -> T) { self.get(path) { (request: Request) -> Response in catching(path, request: request, closure: call) } } func postCatching(_ path: PathComponent..., call: @escaping (Request) throws -> T) { self.post(path) { (request: Request) -> Response in catching(path, request: request, closure: call) } } } private func catching(_ path: [PathComponent], request: Request, closure: @escaping (Request) throws -> T) -> Response { let route = path.map { $0.string }.joined(separator: "/") do { let data = try closure(request) if let d = data as? Data { return Response(status: .ok, body: .init(data: d)) } else { return Response(status: .ok) } } catch let error as CapError { log("\(route): Error \(error)") return Response(status: error.response) } catch { log("\(route): Unhandled error: \(error)") return Response(status: .internalServerError) } } extension PathComponent { var string: String { switch self { case .constant(let value): return value case .parameter(let value): return "<\(value)>" default: return "\(self)" } } }