Skip to main content
This integration requires following Strava’s OAuth flow. Please refer to Strava’s OAuth documentation for more information.

Prerequisites

  • Node.js installed on your machine
  • A Strava Developer account
  • A Notion account

Step 1 - Get Your API Keys

Strava API Key
1
Log in to your Strava account.
2
Go to Strava API.
3
Click on Create An App.
4
Fill in the required details and click Create.
5
Copy the Client ID and Client Secret.
Notion API Key
1
Log in to your Notion account.
3
Click on New Integration.
4
Fill in the required details and click Submit.
5
Copy the generated Internal Integration Token.

Step 2 - Set Up Your Notion Database

1
Create a new database in Notion with the following properties:
  • Name (Title)
  • Distance (Number)
  • Pace (Text)
  • Duration (Text)
  • Elevation Gain (Number)
  • Type (Text)
  • Date (Date)
  • Calories (Number)
2
Share the database with your integration:
  • Click on the three dots in the top-right corner of the page.
  • Under Connections, click on Connect to and search for your integration and invite it.

Step 3 - Project Structure

This step is not necessary but we suggest projects to be structured like so:
project-root/
├── src/
   ├── config/
   └── spotify.js
   └── notion.js
   ├── integrations/
   ├── spotifyIntegration.js
   └── notionIntegration.js
   ├── main.js
   ├── server.js

Step 4 - Create .env file

Create a .env file in your project directory with the following with the following information:
YAML
NOTION_TOKEN='your_notion_integration_token'
NOTION_DATABASE_ID='your_notion_database_id'
STRAVA_CLIENT_ID='your_strava_client_id'
STRAVA_CLIENT_SECRET='your_strava_client_secret'
STRAVA_ACCESS_TOKEN='your_strava_access_token'
STRAVA_REFRESH_TOKEN='your_strava_refresh_token'
Please note that you will have to update your .env file with your STRAVA_ACCESS_TOKEN and STRAVA_REFRESH_TOKEN after completing Strava’s OAuth flow.

Step 5 - Packages

Install the required Node.js packages using the following command:
npm install @notionhq/client dotenv express https
Additionally, create a package.json file and add the following:
JSON
{
    "name": "strava_integration",
    "version": "1.0.0",
    "main": "src/main.js",
    "scripts": {
        "start": "node src/main.js"
    },
    "dependencies": {
        "@notionhq/client": "^1.0.4",
        "dotenv": "^8.6.0",
        "express": "^4.19.2",
        "https": "^1.0.0"
    }
}

Step 6 - OAuth Flow

Create server.js to handle the OAuth flow:
JavaScript
const express = require('express');
const https = require('https');
const querystring = require('querystring');
require('./utils/dotenv');

const app = express();
const PORT = 3000;

app.get('/', (req, res) => {
  res.send('<h1>Strava OAuth Integration</h1><p><a href="/auth">Click here to authorize with Strava</a></p>');
});

app.get('/auth', (req, res) => {
  const authUrl = `https://www.strava.com/oauth/authorize?client_id=${process.env.STRAVA_CLIENT_ID}&response_type=code&redirect_uri=http://localhost:${PORT}/exchange_token&approval_prompt=force&scope=activity:read_all`;
  res.redirect(authUrl);
});

app.get('/exchange_token', (req, res) => {
  const { code } = req.query;

  const postData = querystring.stringify({
    client_id: process.env.STRAVA_CLIENT_ID,
    client_secret: process.env.STRAVA_CLIENT_SECRET,
    code: code,
    grant_type: 'authorization_code'
  });

  const options = {
    hostname: 'www.strava.com',
    path: '/oauth/token',
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  };

  const request = https.request(options, (response) => {
    let data = '';

    response.on('data', (chunk) => {
      data += chunk;
    });

    response.on('end', () => {
      const parsedData = JSON.parse(data);
      if (parsedData.errors) {
        res.status(400).send(`Error: ${parsedData.message}`);
      } else {
        res.send(`Access Token: ${parsedData.access_token}<br>Refresh Token: ${parsedData.refresh_token}`);
      }
    });
  });

  request.on('error', (e) => {
    res.status(500).send(`Error: ${e.message}`);
  });

  request.write(postData);
  request.end();
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Step 7 - Set Up Strava API Client

Create strava.js to set up the Strava API client:
JavaScript
const https = require('https');
const querystring = require('querystring');

// Function to get Strava access token
async function getStravaAccessToken() {
  const postData = querystring.stringify({
    client_id: process.env.STRAVA_CLIENT_ID,
    client_secret: process.env.STRAVA_CLIENT_SECRET,
    refresh_token: process.env.STRAVA_REFRESH_TOKEN,
    grant_type: 'refresh_token'
  });

  const options = {
    hostname: 'www.strava.com',
    path: '/oauth/token',
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  };

  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let data = '';

      res.on('data', (chunk) => {
        data += chunk;
      });

      res.on('end', () => {
        const parsedData = JSON.parse(data);
        if (parsedData.errors) {
          reject(new Error(`Error fetching access token: ${parsedData.message}`));
        } else {
          resolve(parsedData.access_token);
        }
      });
    });

    req.on('error', (e) => {
      reject(e);
    });

    req.write(postData);
    req.end();
  });
}

module.exports = { getStravaAccessToken };

Step 8 - Set Up Notion API Client

Create a notion.js file to set up the Notion API client using the @notionhq/client library. Use your Notion integration token and database ID.
JavaScript
// notion.js
const { Client } = require('@notionhq/client');
const dotenv = require('dotenv');

dotenv.config();

const notion = new Client({ auth: process.env.NOTION_TOKEN });
const databaseId = process.env.NOTION_DATABASE_ID;

module.exports = { notion, databaseId };

Step 9 - Fetch Activity Data From Strava

Create a stravaIntegration.js file to fetch activity data from your Strava account:
JavaScript
const https = require('https');
const { getStravaAccessToken } = require('../config/strava');

// Helper function to make GET requests
async function fetchJson(url, headers) {
  return new Promise((resolve, reject) => {
    const options = {
      headers: headers
    };

    https.get(url, options, (res) => {
      let data = '';

      res.on('data', (chunk) => {
        data += chunk;
      });

      res.on('end', () => {
        resolve(JSON.parse(data));
      });
    }).on('error', (e) => {
      reject(e);
    });
  });
}

// Convert pace from min/km to min/mile
function convertPaceToMinPerMile(paceInMinPerKm) {
  const minPerMile = paceInMinPerKm * 1.60934;
  const minutes = Math.floor(minPerMile);
  const seconds = Math.round((minPerMile - minutes) * 60);
  return `${minutes}:${seconds < 10 ? '0' : ''}${seconds} min/mile`;
}

// Convert distance from meters to miles
function convertMetersToMiles(meters) {
  return meters * 0.000621371;
}

// Fetch activities from Strava
async function getStravaActivities() {
  const accessToken = await getStravaAccessToken();
  const headers = {
    'Authorization': `Bearer ${accessToken}`
  };

  const activitiesResponse = await fetchJson('https://www.strava.com/api/v3/athlete/activities', headers);

  // Log the response to understand its structure
  console.log('Activities Response:', activitiesResponse);

  if (!Array.isArray(activitiesResponse)) {
    throw new Error('Invalid response from Strava API');
  }

  return activitiesResponse.map(activity => {
    const paceInMinPerKm = (activity.moving_time / 60) / (activity.distance / 1000);
    const paceInMinPerMile = convertPaceToMinPerMile(paceInMinPerKm);
    const distanceInMiles = convertMetersToMiles(activity.distance);

    return {
      name: activity.name,
      distance: distanceInMiles,
      pace: paceInMinPerMile,
      duration: new Date(activity.moving_time * 1000).toISOString().substr(11, 8), // Duration in HH:MM:SS
      elevation_gain: activity.total_elevation_gain,
      type: activity.type,
      date: activity.start_date,
      calories: activity.calories
    };
  });
}

module.exports = { getStravaActivities };

Step 10 - Populate Notion Database with Strava Activity Data

Create a notionIntegration.js file to handle populating the Notion database with all Strava activities.
JavaScript
const { Client } = require('@notionhq/client');

// Initialize the Notion client
const notion = new Client({ auth: process.env.NOTION_TOKEN});

// Add data to the Notion database
async function addToNotionDatabase(databaseId, activity) {
  try {
    await notion.pages.create({
      parent: { database_id: databaseId },
      properties: {
        'Name': {
          title: [
            {
              text: {
                content: activity.name
              }
            }
          ]
        },
        'Distance': {
          number: activity.distance
        },
        'Pace': {
          rich_text: [
            {
              text: {
                content: activity.pace
              }
            }
          ]
        },
        'Duration': {
          rich_text: [
            {
              text: {
                content: activity.duration
              }
            }
          ]
        },
        'Elevation Gain': {
          number: activity.elevation_gain
        },
        'Type': {
          rich_text: [
            {
              text: {
                content: activity.type
              }
            }
          ]
        },
        'Date': {
          date: {
            start: activity.date
          }
        },
        'Calories': {
          number: activity.calories || 0 // Default to 0 if undefined
        }
      }
    });
  } catch (error) {
    console.error('Error adding to Notion database:', error);
  }
}

module.exports = { addToNotionDatabase };

Step 11 - Main Script

Create a main.js file that uses the modules created above to execute the integration logic.
JavaScript
require('dotenv').config();

const { getStravaActivities } = require('./integrations/stravaIntegration');
const { addToNotionDatabase } = require('./integrations/notionIntegration');

const NOTION_DATABASE_ID = process.env.NOTION_DATABASE_ID;

// Main function to fill the Notion database with Strava activities
async function fillNotionDatabase() {
  try {
    const activities = await getStravaActivities();

    for (const activity of activities) {
      await addToNotionDatabase(NOTION_DATABASE_ID, activity);
      console.log(\`Added to Notion: Name: ${activity.name}, Distance: ${activity.distance}, Pace: ${activity.pace}\`);
    }
  } catch (error) {
    console.error('Error filling Notion database:', error);
  }
}

fillNotionDatabase();

Step 12 - Run the OAuth Flow

1
Start the OAuth Server:
node src/server.js
2
Open your browser and navigate to http://localhost:3000:
  • You should see a page with a link to authorize with Strava.
3
Click the link to authorize with Strava:
  • This will redirect you to Strava’s authorization page.
4
Authorize the application:
  • Strava will redirect you to a page with an authorization code.
5
The server will exchange the authorization code for an access token and refresh token:
  • The tokens will be displayed on the page.
6
Copy the refresh token and update your .env file with it.

Step 13 - Run the Integration

Execute the integration script by running the following command:
npm start
Customize the code according to your Notion database structure and the properties you want to include for each Strava activity.