Search code examples
swiftvapor

Vapor: Call REST API inside router POST call with parameters


So I'm trying to call a second endpoint in one of my Vapor endpoints. I have one endpoint that is just a get and works good:

router.get("status") { req -> Future<ConnectionResponse> in
  let client = try req.make(Client.self)
  let response = client.get("https://url.com/endpoint/")

  return response.flatMap(to: ConnectionResponse.self, { response in
    return try response.content.decode(ConnectionResponse.self)
  })
}

This returns correctly a ConnectionResponse json. When I try to do the same, but in a POST that needs some parameters, I can't figure out why the compiler doesn't let me run:

router.post("purchase") { req -> Future<ConnectionResponse> in
  return try req.content.decode(PurchaseRequest.self).map(to: ConnectionResponse.self, { response in
    let client = try req.make(Client.self)
    let response = client.get("https://url.com/endpoint/")

    return response.flatMap(to: ConnectionResponse.self, { response in
      return try response.content.decode(ConnectionResponse.self)
    })
  })
}

It fails on the flatMap saying Cannot convert return expression of type 'EventLoopFuture<ConnectionResponse>' to return type 'ConnectionResponse'.

As you can see, the purchase GET call is the same as the status apart from the initial decoding of the POST parameters. What am I doing wrong?


Solution

  • Simply replace

    return try req.content.decode(PurchaseRequest.self).map(to: ConnectionResponse.self, { response in
    

    with

    return try req.content.decode(PurchaseRequest.self).flatMap(to: ConnectionResponse.self, { response in
    

    Complete working code may look like this

    router.post("purchase") { req -> Future<ConnectionResponse> in
      return try req.content.decode(PurchaseRequest.self).flatMap { response in
        let client = try req.make(Client.self)
        let response = client.get("https://url.com/endpoint/")
    
        return response.flatMap { response in
          return try response.content.decode(ConnectionResponse.self)
        }
      }
    }
    

    So what is the difference between map and flatMap?

    flatMap is used if next result is Future<Something> e.g.:

    someDatabaseCall(on: container).flatMap { databaseResult1 in
        /// it will return Future<DatabaseResult>
        /// that's why we use `flatMap` above instead of `map`
        return anotherDatabaseCall(on: container) 
    }
    

    map is for non-future results e.g.:

    someDatabaseCall(on: container).map { databaseResult1 in
        /// it will return non-future result
        /// that's we use `map` above instead of `flatMap`
        return "hello world" // just simple string
    }
    

    What is Future<> ? It's just a promise, like an object with callback.