I am starting using Vapor to run an API, and I couldn't figure out an elegant way of doing the following: (1) load a Fluent relation and (2) wait for the response of an HTTP callback before (3) responding to the original request.
Here is what I ended up coding, after I transformed my real-life entities into planets and stars to maintain the examples from Vapor's documentation. It works™️, but I couldn't achieve a nice chaining of operations ☹️.
func flagPlanetAsHumanFriendly(req: Request) throws -> EventLoopFuture<Planet> {
Planet(req.parameters.get("planetID"), on: req.db)
.unwrap(or: Abort(.notFound))
.flatMap { planet in
// I load the star because I need its ID for the HTTP callback
_ = planet.$star.load(on: req.db).map {
let uri = URI(scheme: "http", host: "localhost", port: 4200, path: "/webhooks/star")
// HTTP Callback
req.client.post(uri) { req in
try req.content.encode(
WebhookDTO(
starId: planet.star.id!,
status: .humanFriendly,
planetId: planet.id!
),
using: JSONEncoder()
)
}
.map { res in
debugPrint("\(res)")
return
}
}
// Couldn't find a way to wait for the response, so the HTTP callback is a side-effect
// and its response is not used in the original HTTP response...
// If the callback fails, my API can't report it.
planet.state = .humanFriendly
return planet.save(on: req.db).map { planet }
}
}
Issue #1. Combining 2 EventLoopFuture
My first issue was that I couldn't find a way to load a parent relationship while keeping the entity in the scope.
Planet(req.parameters.get("planetID"), on: req.db)
.unwrap(or: Abort(.notFound))
.flatMap { planet in // planet is in the scope
return planet.$star.load(on: req.db) // returns a EventLoopFuture<Void>
}
.map {
// I know that star has been loaded but I lost my `planet` reference ☹️
???
}
I assume there is an operator that should be able to return a mix of the 2 EventLoopFuture instances but I couldn't figure it out.
Issue #2. Chaining EventLoopFuture with an auxiliary HTTP request's response
Here as well, I assume I miss an operator that allows me to wait for the request's response, while keeping a reference to the planet, before I respond to the original request.
Help would be welcome on how to achieve this in a nice chaining of operations — if it is possible, of course — and I will more than happy to update Vapor's documentation accordingly.