Push Strava Data to WordPress in Real Time

Strava is an app that allows you to track your runs, hikes and other outdoor activities. It records a lot of data including distance, latitude and longitude, your running pace, etc. Being a full stack web developer, one might expect (rather stereotypically I might add) that I don’t do a lot of running or physical activity in general. However, one of my recent goals has been to get away from the computer, get some exercise, and spend time in the great analog outdoors! And yes… the Strava Webhook and API was exactly the motivation I needed to get out and do just that.

This is the first article in a several part series where we’ll look at how to get data from Strava, how to store it in a WordPress context, and how to access and use it.

Understanding the Strava API

The Strava API uses the common OAuth2 specification for authentication. After obtaining my access token via Postman, my API testing tool of choice, I started hitting some of the other endpoints to see what data I could get back. Two endpoints piqued my interest, Get Activity and Get Activity Streams.

Get Activity is good for getting a lot of information regarding the activity like date, distance, your splits, polyline, cadence, etc etc etc… There’s a decent amount of data that comes back with this request that is fun to look though. Get Activity Streams is the endpoint you want to hit if you want the data that was streamed during your activity. For example, if you want all of the latitude and longitude coordinates of your run, this would be the endpoint you want to hit.

However, before you can get any data, both of these endpoints require you to already have the activity ID. What’s the best way to get that ID? The Strava Webhook API.

Strava Webhook for Real Time Data

For developers to obtain data it is common to send HTTP requests to Rest APIs. We request the data and the API returns it. Webhooks are the opposite; when new data is available it will send it to where we ask it to. Webhooks are great! They are what makes it possible to have my latest activity automatically sent to my website. Let’s look at how this works with Strava.

According to the Strava Documentation for webhooks, the first step is to set up a subscription. Creating a subscription is simple and the docs do a great job of explaining how to do this. But it means I need to build my own API endpoint before I can subscribe to Strava’s Webhook.

Receiving Strava Webhook Data

In WordPress, using the WP Rest API makes it easy to setup my own endpoint to receive the Strava Webhook Data. In my controller class I can create a method that registers the endpoint. Then I can use the rest_api_init hook to call the registration method.

class StravaController {
     ...

     public function registerRoutes() {
        register_rest_route($this->namespace, '/'. $this->resource_name, [
            'methods' => ['GET', 'POST'],
            'callback' => [$this, 'stravaWebhookListener'],
            'permission_callback' => [$this, 'permissionsCallback']
        ]);
    }

    ...
}

// register the endpoint
function prefix_register_custom_endpoint() {
    $strava_controller = new StravaController();
    $strava_controller->registerRoutes();
}
add_action('rest_api_init', 'prefix_register_custom_endpoint');

Now Strava has somewhere to send the data to. First, Strava sends a GET request to the endpoint to verify it is sending the data to the correct place. All subsequent requests will be POST requests and will contain the actual data. The only tricky part lies in this requirement from the docs.

The subscription callback endpoint must acknowledge the POST of each new event with a status code of 200 OK within two seconds. Event pushes are retried (up to a total of three attempts) if a 200 is not returned. If your application needs to do more processing of the received information, it should do so asynchronously.

https://developers.strava.com/docs/webhooks/

Saving data from the Strava Webhook

So why is this requirement tricky? Well, we now have a way to get the Activity ID. And with this ID we can hit the two endpoints I mentioned above and save a lot of data to our database. But to do all of this in 2 seconds is asking a lot. It’s best to send the response first and then handle all the other stuff afterwards. How to handle this requirement varies depending on the programming language but in PHP you can use an output buffer.

// start the output buffer
ob_start();

// send the response to keep Strava happy
echo 'success';
$size = ob_get_length();
header("Content-Encoding: none");
header("Content-Length: $size");
http_response_code(200);
header("Connection: close");

// flush
ob_end_flush();
@ob_flush();
flush();

// now that the response has been sent, you can make requests, save data, 
// and do whatever other processing you need to do

Testing The Endpoint

Testing with web hooks can also be tricky. I’s not exactly feasible to keep going for runs to test that the API endpoint is processing the data correctly. (Although, I could use the exercise). Thankfully, Strava offers sample data, making it easy to send mock requests and handle the data effectively.

For fellow developers reading this, below is a “run in postman” button. It links to a public workspace you can use to test your own endpoint.

Your custom API endpoint needs to:

  • Send the OK response to Strava within 2 seconds
  • Use the object_id sent by Strava to make subsequent requests to get the data you need
  • Save that data to your database
  • Do whatever other processing you have in mind

With my endpoint fulfilling all of the requirements I was ready to subscribe to the webhook. Finally, the last thing to do was to put on my headphones and go for a run. I was happy to see my data waiting for me when I was done!

Speaking of data, in the next part of this series I’ll talk about it in more detail. What data I store, where I store it, and how I use it.