DEV Community

Cover image for List Strava Activities in a Next.js App
Yevheni Kshevitskyi
Yevheni Kshevitskyi

Posted on

List Strava Activities in a Next.js App

Prerequisites

  1. A basic understanding of Next.js and TypeScript
  2. An active Strava account
  3. Access to the Strava API (client ID, client secret, and refresh token)
  4. A GitHub repository for your Next.js project

Step 1: Setting Up Strava API Service

To simplify Strava API integration, we'll create a new service:

import type { Activity } from "@/types";
import axios from "axios";
export const client = axios.create({
baseURL: "https://www.strava.com/api/v3",
});
interface StravaAuthResponse {
token_type: string;
expires_at: number;
expires_in: number;
refresh_token: string;
access_token: string;
}
export async function getAccessToken(): Promise<string> {
const STRAVA_CLIENT_ID = process.env.STRAVA_CLIENT_ID!;
const STRAVA_CLIENT_SECRET = process.env.STRAVA_CLIENT_SECRET!;
const STRAVA_REFRESH_TOKEN = process.env.STRAVA_REFRESH_TOKEN!;
if (!STRAVA_CLIENT_ID || !STRAVA_CLIENT_SECRET || !STRAVA_REFRESH_TOKEN) {
throw new Error(
"Missing required environment variables: STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET, or STRAVA_REFRESH_TOKEN."
);
}
try {
const response = await client.post<StravaAuthResponse>("/oauth/token", {
client_id: STRAVA_CLIENT_ID,
client_secret: STRAVA_CLIENT_SECRET,
refresh_token: STRAVA_REFRESH_TOKEN,
grant_type: "refresh_token",
});
const { access_token, refresh_token, expires_at } = response.data;
console.log("New access token fetched successfully.");
// Optionally, log the expiration time for debugging (convert from UNIX timestamp)
console.log(
"Access token expires at:",
new Date(expires_at * 1000).toISOString()
);
// Optionally update refresh token if it changes
if (refresh_token !== STRAVA_REFRESH_TOKEN) {
console.log("Refresh token has been updated:", refresh_token);
// Save this to your secrets storage if necessary
}
return access_token;
} catch (error) {
console.error(error);
throw new Error("Failed to fetch Strava access token");
}
}
export async function fetchActivities() {
try {
const accessToken = await getAccessToken();
return client.get<Activity[]>("/athlete/activities", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
params: {
per_page: 3,
},
});
} catch (error) {
console.error(error);
throw new Error("Failed to fetch activities");
}
}
export async function fetchExtendedActivities() {
try {
const { data } = await fetchActivities();
const list = await Promise.all(data.map((i) => fetchActivity(i.id)));
return { data: list.map((i) => i.data) };
} catch (error) {
console.error(error);
throw new Error("Failed to fetch activities");
}
}
export async function fetchActivity(id: number) {
try {
const accessToken = await getAccessToken();
return client.get<Activity[]>(`/activities/${id}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
} catch (error) {
console.error(error);
throw new Error("Failed to fetch activity");
}
}
view raw stravaApi.ts hosted with ❤ by GitHub

Step 2: Fetching and Displaying Activities in a Component

To display activities on the frontend, you’ll need to fetch the necessary data and pass it to your React component. In this step, we fetch the data using getStaticProps, which allows the page content and activities to be pre-rendered at build time.

Eg. in pages/index.tsx, the data is fetched by calling fetchExtendedActivities, which retrieves activities data from Strava API. The fetched data is then returned as props, which are passed to the component.

Here’s how you can implement this:

import type { Activity } from "@/types";
import { fetchExtendedActivities } from "@/service/stravaApi";
interface PageProps {
activities: Activity[];
}
export default function Index({ activities }: PageProps) {
return <pre>{JSON.stringify(activities, null, 2)}</pre>
}
export async function getStaticProps() {
const { data: activities } = await fetchExtendedActivities();
return {
props: {
activities,
},
};
}
view raw index.tsx hosted with ❤ by GitHub

Step 4: Configuring GitHub Cron Job

To automate tasks like data fetching, deployments, or updates on a regular basis, you can use a GitHub cron job by configuring a scheduled workflow. This will allow you to trigger your workflow at specific times, such as once a day, at a specific hour, or on particular days of the week.

# Trigger the workflow once a day (adjust the schedule as needed)
schedule:
  - cron: "0 0 * * *" # run the workflow at midnight UTC every day
Enter fullscreen mode Exit fullscreen mode

After the job runs, you can monitor the results and logs from the Actions tab in your GitHub repository. If the cron job fails or produces unexpected results, the logs will help you troubleshoot and adjust the workflow accordingly.


Adding an API Route (Optional)

Since we're using Next.js with SSG mode to generate distribution artefacts for GitHub Pages, we'll call the fetchExtendedActivities method inside getStaticProps. However, if you want to serve data (activities) from Strava API dynamically to your frontend, consider adding an API route in your Next.js app:

import { fetchExtendedActivities } from "@/service/stravaApi";
import type { NextApiRequest, NextApiResponse } from "next";
type ResponseData = unknown[];
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
const response = await fetchExtendedActivities();
res.status(200).json(response.data);
}
view raw strava.ts hosted with ❤ by GitHub

Conclusion

You’ve now set up a Next.js app that pulls in Strava activities, built a backend service, and set up a GitHub cron job to keep things updated regularly. This setup is super flexible, so you can tweak and expand it however you need as your project grows.

Refs.

Image Credits
Photo by Luke Chesser on Unsplash

Top comments (2)

Collapse
 
szatanroch profile image
Roch Szatan

Nice job

Collapse
 
kanapka94 profile image
Adam

Thanks mate! 👏