Vapor — Call an externe web service from your backend

How to execute HTTP call within a Vapor route

Alexandre Cools
6 min readOct 29, 2020
Photo by Amit Jain on Unsplash

Sometimes, when a client call your backend, you need to call another web service to construct your response. For example, if you want to get data from Strava to return activities of a specific user or if you want to get the weather in a specific city, you will have to call an external web service.

As example, I’ll try to get my activities name from the Strava API.

💡 Tips: You can find the final code at the end of the article. 😉

Create the route

First of all, we have to create the route who will be responsible of getting data from the Strava API. My route will be like this: /:userUuid/activities.

If you don’t know how to create Vapor routes and organize them with Controllers, I suggest you to read those articles (Vapor — Create a GET route and Vapor — Use controller to organize your routes) first.

Here’s the starting code of the new route:

Starting route code

Right now, we only get the userUuid from the request and we return an empty String if we find any userUuid parameter, else, we throw a badRequest error.

Note: We will not use the userUuid in our example because we will use an hardcoded Strava access token instead, but in a production environment, you should have the Strava tokens linked to a userUuid. And so, you’ll need this userUuid.

Route return type

Because an external API call is asynchronous (we don’t have the response directly and we don’t know how much time it will take), we can’t return a String (or anything else) like this:

Our route returning a String

The solution is to use an EventLoopFuture of the type we want.

EventLoopFuture: Reference to a value that may not be available yet. — Vapor documentation

Because we want to return an array of activity name, we probably can use a String array, but to go a bit further, we’ll create a class Activity that contains the activity content. By doing this, if you want to add more information about activities, you’ll be able to do it easily.

Here is the basic Activity class we need:

Basic activity class who conform to Content and Codable protocols

This is a basic class with only one attribute: the name of the activity. The only thing to note here is that the class is conforming to the Content Vapor protocol and the Codable protocol.

More informations about Content protocol

If you are curious about this protocol, feel free to Jump to his definition to see more details.

Now we have created our Activity class and we know we have to return an EventLoopFuture, we can update our route definition:

Our route now returning an EventLoopFuture of type [Activity]

Create the Strava API URL

To get Strava data, we need to call the Strava API, so we need to define which URL our API will to call.

Strava API requires that we authenticate before to get our own activities, so we will need to add an authorization header to our request.

Authorization header

Because Strava Authorization header is a bearer, we will need an access token (and probably a refresh token if you push your application in production).

To only focus on the Vapor part, I’ll get a Strava access token and paste it directly to my code as a global variable (if you don’t know how to get started with Strava API, maybe you will appreciated to read this article: …)

⚠️ I do that only during the development phase; if you want to deploy your Vapor application, of course, it’s not a good idea. If you don’t know how to do that for production purpose, feel free to let me a comment and I’ll be happy to help you. ⚠️

Here is how you can construct an HTTPHeaders with an Authorization bearer:

Authorization header

URI

Now we have the request header, we can define our URI with the Strava API URL:

Strava activities URI

Perfect, we have the URL to call and his headers, we can now execute the request and try to get a response from the Strava API.

Execute the request

Get the client and start the GET request

To execute the request, we will need a Client. In fact we already have a client in our Request: request.client. So we can use it.

The Strava call we want to do is a GET method, so we can call the .get function to our client and we’ll pass it the URI and the headers:

Start the request on the client

By doing that, nothing will work. We need to execute this request and to return the transformed response.

Execute the request with flatMapThrowing

To execute the request, we’ll use the flatMapThrowing method directly on our GET function. By doing this, we’ll get the request response and so, we can next get the request status and content.

Execute the request with flatMapThrowing

The advantage of the flatMapThrowing is that we can throw exception if we have to.

Check the status and decode the response

Now we have executed the request, wen can check the response status, throw and error if needed, and then, if no error, decode the response content to get a JSON, and finally transform the JSON to our Activity object.

The response status is available through the response object of course. We want to check if the status is .ok, else we want to throw an error (.unauthorized for example).

Let’s do that by using a guard statment:

Check the response status

Next we can get the response content and decode it (don’t forget to return it):

Get the response content and decode it

If we have any issue, we throw a .badRequest error.

Congratulation, when a client call to our API route (/:userUuid/activities), he get back our activities names as a JSON object:

Activity names returned by our Vapor application from a web browser

Final code

Here is the final code in one place:

func getActivities(request: Request) throws -> EventLoopFuture<[Activity]> {
guard let userUuid = request.parameters.get(“userUuid”) else { throw Abort(.badRequest) }

let headers: HTTPHeaders = HTTPHeaders([(“Authorization”, “Bearer \(accessToken)”)])
let uri: URI = URI(string: stravaActivitiesUrl)
return request.client.get(uri, headers: headers).flatMapThrowing { response in
guard
response.status == .ok else { throw Abort(.unauthorized) }
guard let buffer = response.body else { throw Abort(.badRequest) }
guard let data = String(buffer: buffer).data(using: .utf8) else { throw Abort(.badRequest) }
let decoder: JSONDecoder = JSONDecoder()
do {
return try decoder.decode([Activity].self, from: data)
} catch {
throw Abort(.badRequest)
}
}
}

🚀 What’s next?

Don’t forget to “clap” and share this article if you like it and want to see more content like this! 👏

If you have another way to call external API or if you have any question, I’ll be happy to read you in the comments section right bellow.

Thanks for reading,

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Alexandre Cools
Alexandre Cools

Written by Alexandre Cools

iOS Developer @LunabeeStudio 👨🏻‍💻 — Sport addict 🏊‍♂️🚴‍♂️🏃‍♂️ — Travel enthusiast ✈️

Responses (1)

Write a response