Published: Mon 06 Apr 2020

Build a Netlify-hosted, GitHub auto-deploying, Gatsby-based Website to Display Your Strava Data

I think I have enough keywords in the title to get a few clicks about Gatsby, Netlify, GitHub, and Strava!

Mark Conroy Loughrea Triathlon 2019 - Finishing

As the title of this post suggests, there are 3 parts to this article

  1. Strava and Gatsby
  2. Netlify
  3. GitHub

What we are going to do is create a small website that pulls in a list of all your Strava activities. It then creates a page to list them all, and an individual page for each activity so you can see more details about that activity. For no good reason, we'll also pull in a list of all groups that you are a member of. If you'd just like to see the code that does this, you can get the code from GitHub and you can see the finished site here (it's ugly, but I only care about the data for the point of this article).

1. Strava and Gatsby

I'm going to presume that you know what Gatsby is and how to install it, get a basic site running, and look-up GraphQL queries. If not, Gatsby has some great documentation just waiting for you.

Once you have your site installed and running, you will need to get some data from Strava. To do so, you need the Gatsby Source Strava plugin, which you can get by running npm install gatsby-source-strava --save. You can also find the code for the Gatsby Strava Plugin on GitHub, created by Cédric Delpoux, and with lots of documentation/example code - thanks Cédric.

To get your data from Strava you must click on your profile, then on your settings, and then register for a new application. This will give you some authorisation tokens, which you will need for the next step.

gatsby-source-strava has a command line tool to build your authorisation where you can simply run gatsby-source-strava-token from the root of your project, but I couldn't get it to work, so I just called that file directly from the root of my project: node_modules/gatsby-source-strava/scripts/generate-token.js. This asks you for your access token and guides you through the process.

To fetch the data from Strava, you now need to go to your gatsby-node.js file and we'll use the createPages API from Gatsby to create a page for each of our Strava activities. Putting the following in your gatsby-node.js file will do just that:

  1. const path = require(`path`)
  2.  
  3. module.exports.createPages = ({ graphql, actions }) => {
  4. const { createPage } = actions
  5. return new Promise((resolve, reject) => {
  6. resolve(
  7. graphql(`
  8. {
  9. activities: allStravaActivity {
  10. edges {
  11. node {
  12. activity {
  13. id
  14. }
  15. }
  16. }
  17. }
  18. }
  19. `).then(({ data: { activities } }) => {
  20. activities.edges.forEach(({ node: { activity } }) => {
  21. createPage({
  22. path: `/activities/${activity.id}`,
  23. component: path.resolve("./src/templates/activity/index.js"),
  24. context: {
  25. id: parseInt(activity.id),
  26. },
  27. })
  28. })
  29. })
  30. )
  31. })
  32. }

We now have our data, but we don't have a template to display it. See the line above where we said component: path.resolve("./src/templates/activity/index.js")? Well, that's where Gatsby is going to look for the template to render our activity. So, go to your src and create a folder in there called templates (this is where you can store all your templates). In this create a template for your activities, called activity.js(in my case, I called mine index.js and placed it in a directory called activities) - just make sure the path to it is the same path as we set in our gatsby-node.js a moment ago. Then put the following code in this template (you can amend it to add more information from your activities).

  1. import React, { Fragment } from "react"
  2. import { Link, graphql } from "gatsby"
  3. import Layout from "../../components/layout"
  4. import SEO from "../../components/seo"
  5.  
  6. const ActivityTemplate = ({
  7. data: {
  8. stravaActivity: { activity },
  9. },
  10. }) => (
  11. <Layout>
  12. <SEO title="Home" />
  13. <Fragment>
  14. <h1><strong>Activity Name</strong>: {activity.name}</h1>
  15. <ul>
  16. <li><strong>Activity Type</strong>: {activity.type}</li>
  17. <li><strong>Average Speed</strong>: {activity.average_speed}</li>
  18. <li><strong>Achievement Count</strong>: {activity.achievement_count}</li>
  19. <li><strong>Average Heartrate</strong>: {activity.average_heartrate}</li>
  20. <li><strong>Distance</strong>: {activity.distance}</li>
  21. <li><strong>Elapsed Time</strong>: {activity.elapsed_time}</li>
  22. </ul>
  23. </Fragment>
  24. <Link to="/">Home</Link> |
  25. <Link to="/activities/">Activities</Link> |
  26. <Link to="/clubs/">Clubs</Link> |
  27. <a href="https://www.strava.com/athletes/3331514"> Connect with me on Strava.</a>
  28. </Layout>
  29. )
  30.  
  31. export default ActivityTemplate
  32.  
  33. export const ActivityPageQuery = graphql`
  34. query($id: Float) {
  35. stravaActivity(activity: { id: { eq: $id } }) {
  36. activity {
  37. average_speed
  38. name
  39. achievement_count
  40. average_heartrate
  41. distance
  42. elapsed_time
  43. type
  44. }
  45. }
  46. }
  47. `

When you now run gatsby build from your terminal, you should be able to go to localhost:8000/activities/XYZ(where XYZ is the id of your activity)

To build a page to list all of your activities, and link each to it's respective page, in src/pages create a file called activities.js and place the following code in it

  1. import React, { Fragment } from "react"
  2. import { Link, graphql } from "gatsby"
  3. import Layout from "../components/layout"
  4. import SEO from "../components/seo"
  5.  
  6. const ActivitiesPage = ({ data }) => (
  7. <Layout>
  8. <SEO title="Mark Conroy Strava Activities" />
  9. <h1>My {data.allStravaActivity.edges.length} Activities</h1>
  10. <p>Click an activity title for more information about that activity</p>
  11. <ol>
  12. {data.allStravaActivity.edges.map(edge => (
  13. <Fragment>
  14. <li>
  15. <h2><Link to={`/activities/${edge.node.activity.id}`}>{edge.node.activity.name}</Link></h2>
  16. <ul>
  17. <li>Completed: {edge.node.activity.when_date} at {edge.node.activity.when_time}</li>
  18. <li>Moving time: {edge.node.activity.moving_time} seconds</li>
  19. <li>Distance of {edge.node.activity.distance} metres</li>
  20. </ul>
  21. </li>
  22. </Fragment>
  23. ))}
  24. </ol>
  25. <Link to="/">Home</Link> |
  26. <Link to="/activities/">Activities</Link> |
  27. <Link to="/clubs/">Clubs</Link> |
  28. <a href="https://www.strava.com/athletes/3331514"> Connect with me on Strava.</a>
  29. </Layout>
  30. )
  31.  
  32. export default ActivitiesPage
  33.  
  34. export const ActivitiesPageQuery = graphql`
  35. {
  36. allStravaActivity(sort: {order: DESC, fields: activity___start_date}) {
  37. edges {
  38. node {
  39. activity {
  40. name
  41. id
  42. when_date: start_date(formatString: "MMMM DD, YYYY")
  43. when_time: start_date(formatString: "H:m")
  44. type
  45. moving_time
  46. distance
  47. }
  48. }
  49. }
  50. }
  51. }
  52. `

And to get a list of your clubs and link them directly to their Strava page (rather than an internal page on your site), create a file called clubs.js in src/pages with the following code.

  1. import React, { Fragment } from "react"
  2. import { Link, graphql } from "gatsby"
  3. import Layout from "../components/layout"
  4. import SEO from "../components/seo"
  5.  
  6. const ClubsPage = ({ data }) => (
  7. <Layout>
  8. <SEO title="Mark Conroy Strava Activities" />
  9. <h1>My {data.stravaAthlete.athlete.clubs.length} Clubs</h1>
  10. <p>Click on a club name to go to that club's Strava page.</p>
  11. <ol>
  12. {data.stravaAthlete.athlete.clubs.map(club => (
  13. <Fragment>
  14. <li>
  15. <a href={`https://www.strava.com/clubs/${club.url}`}>{club.name} {!club.private ? true : '- Private Group'}</a>
  16. </li>
  17. </Fragment>
  18. ))}
  19. </ol>
  20. <Link to="/">Home</Link> |
  21. <Link to="/activities/">Activities</Link> |
  22. <Link to="/clubs/">Clubs</Link> |
  23. <a href="https://www.strava.com/athletes/3331514"> Connect with me on Strava.</a>
  24. </Layout>
  25. )
  26.  
  27. export default ClubsPage
  28.  
  29. export const ClubsPageQuery = graphql`
  30. {
  31. stravaAthlete {
  32. athlete {
  33. clubs {
  34. name
  35. url
  36. private
  37. }
  38. }
  39. }
  40. }
  41. `

All of this will create a .strava directory in your root directory beside your .cache directory. Make sure you add it to your .gitignore file, as it will be created by Netlify when we deploy the site, and also because it contains your access token information and you don't want that publicly available.

That'll do for the Gatsby part!

2. Netlify

Everytime I try out Netlify (and it's only been a few times), I'm constantly amazed at how great it is. I remember Phil Hawksworth giving a great presentation at Frontend United 2019 saying basically Netlify aims to get everything that is difficult about hosting and make it easy. I think they have nailed that principle.

When it comes to hosting an Gatsby site on Netlify, you just need to create a new site in Netlify, choose to deploy from a GitHub repository, and select the repository. From there, you can choose what directory to deploy from. In this case choose public.

Next, you need to create an environment variable to put the sensitive information from your access token in (that we gitignored a moment ago). To do so:

  • Go to your Netlify site
  • Click on the "Settings" tab
  • Click on "Build & Deploy"
  • Click on "Environment"
  • Add a new variable called GATSBY_SOURCE_STRAVA_TOKEN
  • Paste everything that is in your ./.strava/token.json file into the "Value" field
  • You're done

You can now deploy your site by clicking on the "Deploys" tab and then clicking "Trigger deploy". This will run the npm build command from your package.json file and deploy the site from your public directory.

3. GitHub

Now that we have everything working, let's set up a GitHub action to autodeploy the site every 3 hours. Note, this is not the recommended way to do things with Strava - the recommended way is to set up a webhook so everytime a new activity is added, the site gets re-built, rather than building just for the sake of it every 3 hours. For my purposes, I wanted to see how GitHub actions worked, so did it this way. This will also break after 2 days as your access token will be out of date, but I'm not going to go into auto-regenerating new tokens, since this was just a playground project for me. Here's what you need to do:

  • In Netlify - yes, Netlify, not GitHub - click on "Settings" then "Build & Deploy" and scroll down to "Build hooks".
  • Add a new build hook. I called mine "Redeploy site". This will give you a URL, and anytime that URL is hit, the site will redeploy.
  • In your repo, create a folder called workflows at the root leve. This is where we will store all our workflow actions for GitHub; in our case, we'll have only one.
  • Inside this, add a file called main.yml, where we will add our action.
  • Add the following code to the main.yml file which will call a cron function to hit your redeploy endpoint every 3 hours, replacing ADD_BUILD_HOOK_URL_HERE with the URL of your build hook.
    1. name: Trigger Netlify Build
    2.   on:
    3.   schedule:
    4.   - cron: '* */3 * * *' # every 3 hours
    5.   jobs:
    6.   build:
    7.   name: Redeploy Site
    8.   runs-on: ubuntu-latest
    9.   steps:
    10.   - name: Curl request
    11.   run: curl -X POST -d {} ADD_BUILD_HOOK_URL_HERE
    12.  

That's It!

A Gatsby site, that pulls in data from Strava, is hosted on Netlify, and uses GitHub actions to auto-deploy every 3 hours. Here is the finished site, and here is the complete codebase.

Comments

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.