Unlock Personalized Fitness: Building Your AI-Powered Strava Training Coach with SvelteKit & TypeScript
A step-by-step guide to integrating Strava data, AI analysis, and SvelteKit for automated, intelligent workout suggestions.
Highlights
- Seamless Strava Integration: Learn how to authenticate users via Strava's OAuth 2.0 and securely manage access tokens within your SvelteKit application using tools like Auth.js.
- Real-time Activity Monitoring: Discover the best methods (Webhooks preferred, Polling as fallback) to continuously detect and fetch new Strava activities for timely analysis.
- AI-Driven Personalization: Understand how to leverage AI models (like GPT or Gemini) to analyze historical Strava data and generate truly personalized training recommendations tailored to user performance and goals.
Building a web application that connects to Strava, analyzes user activity data, and suggests personalized training plans requires integrating several modern technologies. This guide provides a comprehensive walkthrough on how to achieve this using SvelteKit, a powerful web framework, and TypeScript for enhanced code quality and type safety. We'll cover everything from user authentication and Strava API integration to data processing and leveraging Artificial Intelligence (AI) for insightful training recommendations.
Step 1: Setting Up Your SvelteKit Foundation with TypeScript
Bootstrapping Your Development Environment
The first step is to create a new SvelteKit project configured to use TypeScript. SvelteKit provides a seamless development experience for building full-stack applications, including features like server-side rendering (SSR) and API routes, which are crucial for this project.
Initializing the Project
Open your terminal and run the following commands:
# Create a new SvelteKit project
npm create svelte@latest my-strava-ai-coach
# Navigate into the project directory
cd my-strava-ai-coach
# Install dependencies
npm install
During the interactive setup process, make sure to select the options for enabling TypeScript. This command scaffolds a basic SvelteKit application structure with TypeScript configured.
Why SvelteKit and TypeScript?
- SvelteKit: Offers a robust framework for building both the frontend UI and the backend API endpoints within a single project. Its file-based routing and server capabilities simplify development.
- TypeScript: Adds static typing to JavaScript, catching potential errors during development and improving code maintainability, especially important when handling complex data structures from the Strava API and AI models.
Configuring API Routes
SvelteKit allows you to create backend API endpoints easily. You'll create files named +server.ts within your src/routes/api directory (or subdirectories). These endpoints will handle server-side logic such as authentication callbacks, interacting with the Strava API, processing data, and communicating with your AI model.
For example, you might structure your API routes like this:
src/routes/api/auth/strava/+server.ts: Handles Strava OAuth flow.src/routes/api/webhooks/strava/+server.ts: Receives Strava webhook notifications.src/routes/api/activities/analyze/+server.ts: Triggers AI analysis for activities.
Step 2: Implementing User Authentication
Securely Managing User Sign-Up and Login
Your application needs a way for users to sign up and log in. While you could build this from scratch, using a dedicated authentication library simplifies the process and enhances security. Auth.js (formerly NextAuth.js) is highly recommended as it officially supports SvelteKit and offers pre-built providers, including one for Strava.
Integrating Auth.js
Install Auth.js into your SvelteKit project:
npm install @auth/sveltekit @auth/core
Configure Auth.js in your SvelteKit hooks file (src/hooks.server.ts) or directly within an API route handler. You'll need to define providers (like email/password or OAuth providers like Strava).
// src/hooks.server.ts
import { SvelteKitAuth } from "@auth/sveltekit";
import Strava from "@auth/sveltekit/providers/strava";
import { STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET, AUTH_SECRET } from "$env/static/private"; // Use environment variables
export const handle = SvelteKitAuth({
providers: [
Strava({
clientId: STRAVA_CLIENT_ID,
clientSecret: STRAVA_CLIENT_SECRET,
authorization: { params: { scope: "read,activity:read_all" } } // Request necessary permissions
}),
// Add other providers (e.g., Email) if needed
],
secret: AUTH_SECRET,
callbacks: {
async jwt({ token, account }) {
// Persist the OAuth access_token and refresh_token to the token right after signin
if (account) {
token.accessToken = account.access_token;
token.refreshToken = account.refresh_token;
token.expiresAt = account.expires_at;
token.stravaId = account.providerAccountId; // Store Strava User ID
}
// TODO: Handle token refresh
return token;
},
async session({ session, token }) {
// Send properties to the client, like an access_token and user ID from the token
session.accessToken = token.accessToken as string;
session.refreshToken = token.refreshToken as string;
session.user.id = token.sub; // Use <code>sub which is typically the user ID
session.stravaId = token.stravaId as string;
session.error = token.error as string; // Pass potential errors
return session;
}
}
});
This setup handles the basic authentication flow. You'll need a database (like PostgreSQL, MongoDB, or Supabase) to store user information persistently, linking your internal user ID with their Strava ID and tokens.
User Interface for Sign-Up/Login
Create Svelte components (e.g., src/routes/login/+page.svelte) with buttons or forms that trigger the Auth.js sign-in functions. For Strava, this will initiate the OAuth flow.
Step 3: Connecting the User's Strava Profile via OAuth 2.0
Authorizing Your App to Access Strava Data
To access a user's Strava activities, your application must obtain their explicit permission using the OAuth 2.0 protocol. Auth.js significantly simplifies this process when using its Strava provider.
Initiating the OAuth Flow
In your SvelteKit frontend (e.g., a user dashboard page), provide a button that triggers the Strava sign-in process managed by Auth.js:
<!-- Example button in a Svelte component -->
<script lang="ts">
import { signIn } from "@auth/sveltekit/client";
</script>
<button on:click={() => signIn('strava')}>
Connect Strava Account
</button>
Clicking this button redirects the user to Strava's authorization screen. Here, they grant your application permission to access their data based on the scopes you requested (e.g., read,activity:read_all).
Handling the Callback and Tokens
After authorization, Strava redirects the user back to your application's configured callback URL. Auth.js intercepts this callback, exchanges the authorization code for access and refresh tokens, and stores them securely (often in the JWT session token initially, which should then be persisted securely, likely in your database linked to the user).
- Access Token: A short-lived token used to make authenticated API requests to Strava.
- Refresh Token: A long-lived token used to obtain new access tokens when the current one expires, without requiring the user to re-authenticate.
Crucially, store these tokens securely. Never expose the refresh token or client secret on the client-side. Manage token storage and refresh logic on your server (within SvelteKit API routes or dedicated backend services).
Registering Your Application with Strava
Before implementing OAuth, you must register your application on the Strava Developer Dashboard. This provides you with the necessary Client ID and Client Secret required for the Auth.js configuration and the OAuth flow. Set the "Authorization Callback Domain" to your application's domain.
Step 4: Continuously Monitoring for New Strava Activities
Keeping Your Application Data Up-to-Date
To provide timely analysis and suggestions, your application needs to know when a user uploads a new activity to Strava. There are two primary methods: Webhooks (recommended) and Polling.
Method 1: Strava Webhooks (Preferred)
Webhooks are the most efficient method. Strava sends a notification (a POST request) to a specified endpoint in your application whenever a subscribed event occurs (like a new activity upload).
- Setup: Create a dedicated API endpoint in your SvelteKit app (e.g.,
src/routes/api/webhooks/strava/+server.ts) to receive these notifications. - Subscription: Use the Strava API to subscribe your webhook endpoint to user activity events. This involves an initial validation handshake. See the Strava Webhooks Documentation.
- Processing: When your endpoint receives a notification, it typically contains IDs related to the event (e.g., activity ID, owner ID). Use this information to fetch the full activity details using the Strava API (Step 5).
Method 2: API Polling (Fallback)
If webhooks are unsuitable or difficult to implement (e.g., during local development without a publicly accessible URL), you can periodically poll the Strava API.
- Implementation: Create a scheduled task (e.g., using
node-cronor a serverless function with a timer trigger) that runs periodically (e.g., every 15 minutes). - Logic: For each connected user, the task fetches their recent activities from the Strava API (e.g., using the
/athlete/activitiesendpoint with parameters likeafterbased on the timestamp of the last fetched activity). - Processing: Compare the fetched activities with those already stored in your database to identify new ones. Process any new activities found.
Caution: Polling is less efficient, consumes more resources, and is subject to Strava API rate limits. Use it judiciously.
Comparing Monitoring Methods
This table summarizes the key differences between Webhooks and Polling:
| Feature | Webhooks | Polling |
|---|---|---|
| Efficiency | High (Event-driven) | Low (Requires continuous checking) |
| Timeliness | Near Real-time | Delayed (Based on polling interval) |
| Resource Usage | Low (Receives data passively) | High (Actively makes API requests) |
| Setup Complexity | Moderate (Requires public endpoint, subscription management) | Low (Requires scheduling mechanism) |
| Rate Limit Impact | Minimal (Only fetches data upon notification) | Higher (Regular requests count towards limits) |
Step 5: Fetching and Structuring Strava Activity Data
Retrieving Detailed Workout Information
Once notified of a new activity (via webhook or polling), you need to fetch its detailed data from the Strava V3 API. You'll use the user's stored access token to authenticate these requests.
Using the Strava API
The primary endpoint for fetching details of a specific activity is /activities/{id}. You'll need the activityId obtained from the webhook payload or the list of recent activities.
Example API call using fetch within a SvelteKit API route:
// Example inside an API route (e.g., processing a webhook) async function fetchActivityDetails(accessToken: string, activityId: number): Promise<any> { try { const response = await fetch(<code>https://www.strava.com/api/v3/activities/${activityId}, { headers: { Authorization:Bearer ${accessToken}, }, }); if (!response.ok) { // Handle errors, e.g., invalid token, rate limits console.error(Strava API Error: ${response.status} ${await response.text()}); // Potentially trigger token refresh logic here return null; } const activityData = await response.json(); return activityData; } catch (error) { console.error("Error fetching activity details:", error); return null; } }
Handling Data with TypeScript
The data returned by the Strava API can be complex. Using TypeScript helps manage this:
- Define Interfaces/Types: Create TypeScript interfaces that match the structure of the Strava API responses (e.g.,
StravaActivity,Athlete, etc.). This provides type safety and autocompletion. - API Wrappers: Consider using or creating a TypeScript wrapper library for the Strava API (like
strava-typescript-apifound on GitHub) to simplify API interactions and type handling.
Data Storage and Structure
It's highly recommended to store the fetched Strava activity data in your own database. This allows for:
- Faster Access: Avoids repeated calls to the Strava API for historical data analysis.
- Offline Analysis: Enables analysis even if the Strava API is temporarily unavailable.
- Data Structuring: You can structure or flatten the data in a way that's optimized for your AI analysis pipeline. Raw JSON from APIs can sometimes be nested or complex.
Step 6: AI-Powered Analysis and Training Suggestions
Turning Data into Actionable Insights
This is the core intelligence of your application. Here, you'll use AI models to analyze the user's historical Strava data (stored in your database) and generate personalized training suggestions.
Choosing an AI Model
You have several options for the AI component:
- Large Language Models (LLMs): Services like OpenAI's GPT-4 or Google's Gemini can analyze structured text descriptions of activities and generate human-like training advice. You would send relevant historical data and the latest activity details, along with a carefully crafted prompt.
- Custom Machine Learning Models: You could train your own models (e.g., using TensorFlow.js, scikit-learn) if you have specific analytical goals (e.g., predicting fatigue, optimizing interval times based on heart rate zones). This requires more expertise in data science.
- Hybrid Approaches: Combine statistical analysis of numerical data (pace, heart rate, power) with LLMs for interpreting trends and generating textual suggestions.
Strava's own "Athlete Intelligence" feature demonstrates the power of AI in summarizing activities, providing a benchmark for the type of insights you might aim for.
AI is increasingly integrated into fitness applications for personalized insights.
Preparing Data for AI
Raw activity data needs processing before being fed to an AI model:
- Feature Engineering: Extract relevant metrics (e.g., average pace, max heart rate, time in zones, elevation gain, training load).
- Structuring Data: Format the data consistently. For LLMs, this might involve creating textual summaries or JSON structures representing past performance and recent activities.
- Contextualization: Include user goals (if collected) and historical trends to provide context for the AI.
Prompt Engineering (for LLMs)
If using an LLM, designing effective prompts is crucial. Your prompt should clearly instruct the AI on its task:
"You are an expert running coach AI. Analyze the following recent Strava activity data for a user aiming to improve their 5k time, considering their past 4 weeks of training history. Suggest 1-2 specific workout sessions for the upcoming week, focusing on balancing intensity and recovery.
User Goal: Improve 5k time (current PR: 25:00)
Recent Activity:
{
"type": "Run",
"date": "2025-05-04",
"distance_meters": 5100,
"duration_seconds": 1560, // 26 minutes
"average_heart_rate": 165,
"max_heart_rate": 180,
// ... other relevant metrics
}
Past 4 Weeks Summary:
{
"total_runs": 12,
"total_distance_km": 55,
"average_weekly_distance_km": 13.75,
"long_run_max_km": 10,
"intensity_distribution": { "zone1_percent": 30, "zone2_percent": 40, "zone3_percent": 20, "zone4_percent": 10, "zone5_percent": 0 }
// ... other summary stats
}
Provide the workout suggestions in a structured format (e.g., Warmup, Main Set, Cooldown)."
Integrating AI Analysis
Your SvelteKit backend (API routes) will orchestrate the AI analysis:
- Retrieve relevant historical and recent activity data from your database for the user.
- Format the data and construct the prompt (if using an LLM).
- Make an API call to your chosen AI service (e.g., OpenAI API, Google AI API) or execute your custom model.
- Receive the analysis and training suggestions from the AI.
- Store the suggestions in your database, linking them to the user.
- Notify the frontend or make the suggestions available for the user to view.
AI can assist in generating tailored workout plans based on user data.
Step 7: Building the Frontend with SvelteKit
Displaying Data and Suggestions to the User
SvelteKit excels at building reactive and performant user interfaces. You'll create Svelte components (.svelte files) to display the user's Strava activities, performance trends, and the AI-generated training suggestions.
Key UI Components
- Dashboard: An overview page showing recent activities, upcoming suggested workouts, and perhaps performance charts.
- Activity List/Feed: A display of past Strava activities fetched via your backend.
- Activity Detail View: Shows detailed metrics for a specific activity, potentially including a map visualization of the route (using libraries like Leaflet or MapLibre GL JS with polyline decoding).
- Training Plan Section: Displays the AI-generated workout suggestions.
- User Profile/Settings: Allows users to manage their profile and Strava connection status.
Data Loading in SvelteKit
Use SvelteKit's load functions in your page components (+page.server.ts or +page.ts) to fetch data from your backend API routes before the page renders. This ensures data is available when the component mounts.
Visualizing Training Plan Focus
A radar chart can effectively visualize the characteristics of suggested training plans, helping users understand the focus of the AI's recommendations across different dimensions.
The chart above provides a hypothetical comparison of different AI-suggested workout plans. For example, 'Plan A (Endurance Focus)' emphasizes duration and recovery, while 'Plan C (Intensity Focus)' scores higher on intensity and short bursts but lower on duration. This visualization helps users quickly grasp the nature of the suggested training.
Application Architecture Overview
Visualizing the Workflow
The following mind map illustrates the high-level architecture and data flow of the Strava AI Coach application built with SvelteKit:
This map shows how the user interacts with the SvelteKit frontend, which communicates with the backend API routes. These routes handle authentication, interact with the Strava API and the AI service, and manage data storage in the database.
Building a Fitness Dashboard: Insights from Experience
Learning from Others: Integrating Strava API
Understanding how others have approached similar projects can be invaluable. This video provides a detailed walkthrough of building a fitness dashboard using the Strava API, covering aspects like setting up a Strava application, handling authorization, and making API calls. While the specific tech stack might differ, the core concepts of interacting with the Strava API, managing authentication, and handling data are highly relevant to building your AI coach.
Key takeaways often include the nuances of the OAuth flow, the structure of data returned by various endpoints (like activities and athlete profiles), and strategies for handling API rate limits. Watching how data is fetched and processed can provide practical insights for implementing Steps 3, 5, and 7 described above.
Frequently Asked Questions (FAQ)
How do I handle Strava API rate limits?
Strava imposes rate limits on API requests (e.g., per 15 minutes and daily). Check the Strava API Rate Limits documentation for current limits. To manage them:
- Prefer Webhooks over Polling to reduce unnecessary requests.
- Implement caching for data that doesn't change often (e.g., user profile).
- Use efficient polling intervals if you must poll.
- Implement exponential backoff strategies if you hit a rate limit.
- Fetch only the data you need.
How should I securely store Strava access and refresh tokens?
Never store tokens directly in the frontend code or local storage. Store them securely on the server-side:
- Use a secure database.
- Encrypt the tokens at rest in the database.
- Ensure only authorized backend processes can access the tokens.
- Manage token access tightly using environment variables or secrets management services for database credentials and API keys.
- Refresh tokens should be handled exclusively server-side.
What AI model is best for generating training suggestions?
The "best" model depends on your specific needs and resources:
- LLMs (GPT-4, Gemini): Excellent for generating human-readable, context-aware suggestions based on textual descriptions and structured data. Requires careful prompt engineering and managing API costs.
- Custom ML Models: Offer more control and potentially deeper insights into specific metrics (e.g., fatigue prediction) but require significant data science expertise, training data, and infrastructure.
- Hybrid: Combining statistical analysis with LLM interpretation can offer a good balance.
Start with an LLM via API for faster prototyping, then consider custom models if needed for more specialized analysis.
How do I handle Strava token expiration and refresh?
Strava access tokens expire (typically after 6 hours). You must use the refresh token to obtain a new access token before the old one expires or when an API call fails due to an invalid token.
- Store the `expires_at` timestamp provided during the initial OAuth flow along with the access and refresh tokens.
- Before making an API call, check if the access token is close to expiring (e.g., within the next 5-10 minutes).
- If expired or close to expiring, make a request to Strava's `/oauth/token` endpoint with `grant_type=refresh_token` and your stored refresh token.
- Strava will return a new access token, a new refresh token (use this one going forward!), and a new `expires_at` time.
- Update the stored tokens in your secure database.
- Implement robust error handling for API calls to detect expired/invalid tokens and trigger the refresh flow.
- Auth.js can often help manage parts of this flow, but custom logic might be needed depending on your storage strategy.
Recommended Next Steps
Explore these related topics for deeper insights:
- How to implement Strava Webhooks in a SvelteKit application?
- What are the best practices for securely storing API tokens and user data in SvelteKit apps?
- How can I use the OpenAI or Google Gemini API for analyzing fitness data and generating personalized plans?
- How can I visualize GPS route data from the Strava API using SvelteKit and Leaflet?
References
Last updated May 5, 2025