惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Scott Helme
Scott Helme
N
Netflix TechBlog - Medium
AI
AI
Security Latest
Security Latest
GbyAI
GbyAI
P
Proofpoint News Feed
Y
Y Combinator Blog
A
Arctic Wolf
G
Google Developers Blog
U
Unit 42
爱范儿
爱范儿
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
Vulnerabilities – Threatpost
Know Your Adversary
Know Your Adversary
Cisco Talos Blog
Cisco Talos Blog
T
Tor Project blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
C
Check Point Blog
B
Blog RSS Feed
The GitHub Blog
The GitHub Blog
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 【当耐特】
博客园 - Franky
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
Cisco Blogs
云风的 BLOG
云风的 BLOG
NISL@THU
NISL@THU
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
Latest news
Latest news
L
LINUX DO - 最新话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
美团技术团队
WordPress大学
WordPress大学
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
The Hacker News
The Hacker News
Simon Willison's Weblog
Simon Willison's Weblog
V
V2EX
Project Zero
Project Zero
博客园_首页

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Building a Ride Analysis Web App with Antigravity and the Strava API
Akira Kikusa · 2026-05-16 · via DEV Community

Introduction

I ride bikes a lot these days, and like many cyclists I use Strava to log my rides. Even on the free tier it automatically syncs with smartwatches, cycling computers, and smart trainers, which is plenty for just keeping a record. But the more I learned about training, the more I wanted richer analytics and planning — and most of that lives behind Strava's paid subscription.

While poking around, I noticed Strava offers a public Strava API. That was enough motivation to build my own analytics on top of it.

Concretely, I wanted these features:

  • Ride and training management
    • Review past rides … ①
    • Visualize power and heart-rate distributions by intensity zone … ②
    • View power and heart-rate traces color-coded by intensity zone … ③
    • Show recent training load and fatigue to get a sense of current condition … ④
  • Ride and training planning
    • Build training plans by intensity and goal for upcoming events … ⑤
    • Recommend the next workout based on recent condition (training load and fatigue) … ⑥

I built the app using Antigravity. There are two parts: the analytics app itself, and an agent that suggests the next workout based on recent condition. This post covers the analytics app. The agent will be a separate post.

Overview of the App: "PerfRide"

The result is a web app called PerfRide that uses the Strava API to manage ride records and training plans for road cycling.

PerfRide 🚴

A performance management toolkit for road cyclists, powered by the Strava API.

Simulate climbs, optimize race pacing, plan periodized training, and track your fitness — all in one app.

Features

📊 Dashboard

Connect with Strava to view your recent rides, weekly training summary, and fitness progress chart (CTL / ATL / TSB). Includes per-ride detail with heart rate zones, power profile, and elevation overlay.

🏔️ Climb Simulator

Predict climbing times based on power, weight, and real segment data. Uses physics-based simulation (air resistance, rolling resistance, drivetrain loss). Search segments by map or use your Strava starred segments.

🎯 Pace Optimizer

Calculate optimal pacing strategy for time trials based on course elevation profile. Based on the research paper "A numerical design methodology for optimal pacing strategy in the individual time trial discipline of cycling" (Sports Engineering, 2025).

📅 Training Planner

Generate periodized training plans for your target race…

The main features (including some experimental ones) are:

Feature Description Maps to Needs Strava Experimental
Dashboard Activity list, Fitness/Fatigue/Form charts ①, ②, ③, ④ Yes
Weekly Plan This week's workout plan ⑤, ⑥ Partial
Climb Simulator Predicts climb time from power and weight using physics-based math Partial
Pace Optimizer Computes optimal pacing based on a course profile Partial
Training Planner Generates a phased training plan working backward from a goal race No
Settings User parameters: FTP, weight, max HR, etc. No

PerfRide landing page
PerfRide landing page

Dashboard

Once connected to Strava, the dashboard shows your recent rides and a weekly summary (ride count, distance, elevation gain, moving time). It also derives fitness metrics from roughly the last 13 weeks of activity data and visualizes the following three indicators along with trends in distance and elevation:

  • Fitness (CTL: Chronic Training Load): Long-term accumulated training load. Higher = better aerobic base.
  • Fatigue (ATL: Acute Training Load): Fatigue from recent training. Higher = more tired.
  • Form (TSB: Training Stress Balance): Fitness − Fatigue. Indicates how race-ready you are (+10 to +25 is generally considered optimal for racing).

Clicking on a ride drills into details like heart-rate zone distribution, power profile, and elevation chart.

Dashboard page
Dashboard page

Weekly Plan

The Weekly Plan shows "what training you should do this week" laid out day by day. The Dashboard surfaces a card for just today's session, and the Weekly Plan page shows the full week (session type, target TSS, status) along with past plan history.

Weekly plan page
Weekly plan page

The plan itself is generated by a separate agent that runs early every Monday morning and builds out the following week automatically. It looks at recent Fitness/Fatigue/Form and user settings, then proposes sessions aligned with a periodization cycle (Base → Build → Peak → Taper). I'll cover the agent in a separate post.

About the experimental features

The three features below — Climb Simulator, Pace Optimizer, Training Planner — are currently marked as experimental. They work, but the UX flow and parameters are still rough. I built them quickly with help from Antigravity and LLMs, referenced some papers, but haven't fully polished them yet.

Climb Simulator estimates climb time. Enter power, weight, distance, and elevation gain, and it computes a predicted time using physics-based math. If you're connected to Strava, you can also estimate times for your starred segments.

Climb Simulator page
Climb Simulator page

Pace Optimizer computes optimal power distribution based on a course profile. It's based on a 2025 paper, "A numerical design methodology for optimal pacing strategy in the individual time trial discipline of cycling". The core idea: push higher power on climbs and lower power on descents to shorten total time while keeping Normalized Power (NP) roughly constant. Intuitively, at lower speeds on climbs, aerodynamic drag is smaller, so extra watts translate more directly into time saved. There's still room to improve, but it's a feature grounded in recent research.

Pace Optimizer page
Pace Optimizer page

Training Planner generates a phased training plan working backward from a goal race date. The phases are split roughly like this:

Phase Share of time Purpose
Base 35% Build aerobic foundation
Build 1 25% Tempo and threshold work
Build 2 20% VO2max and high-intensity
Peak 10% Race-specific simulation
Taper Remainder Recovery and tuning

Training Planner page
Training Planner page

Architecture

System overview

The app is structured as frontend + BFF + external APIs, with the next-workout-suggestion agent split off as a separate service.

The frontend doesn't call the Strava API directly. Instead it goes through Next.js API Routes (BFF pattern), which lets me keep access tokens, refresh logic, and a cache layer on the server. The browser never sees secrets, and rate-limit-friendly caching only needs to live in one place.

There's no database for ride history — Strava is the source of truth. Activity lists, ride details, and segment info are fetched from the Strava API on demand and cached for a short window inside the API Routes (BFF layer). That removes the need for a sync job and doubles as rate-limit protection. The only data I persist app-side is per-user settings, tokens, and weekly plans, all stored in Cloud Storage. No schema migrations, no per-environment ops; for personal-scale use this setup is plenty.

The agent that suggests and adjusts the next workout combines "LLM + domain logic," and Python has a stronger library ecosystem for that (and I'm more comfortable in Python for ML work), so I split it out as its own FastAPI service on Cloud Run. Details in a future post.

┌─────────────────────────────────────────────────────┐
│                    Frontend                          │
│  Next.js 16 (App Router) + TypeScript + React 19    │
│  - Auth: NextAuth.js                                 │
│  - Charts: Recharts                                  │
│  - Maps: Leaflet + React-Leaflet                     │
└─────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────┐
│               API Routes (BFF)                       │
│  Next.js API Routes (server-side)                    │
│  - Strava access token management / refresh          │
│  - Cloud Storage read/write (settings/cache/plans)   │
└─────────────────────────────────────────────────────┘
            │                          │
            ▼                          ▼
┌─────────────────────────────┐  ┌─────────────────────────┐
│     External Services        │  │        Storage          │
│  - Strava API (OAuth 2.0)    │  │  - Cloud Storage        │
│  - Nominatim (geocoding)     │  │   (JSON objects only)   │
└─────────────────────────────┘  └─────────────────────────┘
            │
            ▼
┌─────────────────────────────────────────────────────┐
│   Agent Service (covered in a future post)           │
│  Python / FastAPI / Google ADK + Gemini             │
│  - Daily Recommendation                              │
│  - Weekly Plan Generation                            │
└─────────────────────────────────────────────────────┘

Enter fullscreen mode Exit fullscreen mode

The main tech stack:

Category Stack
Frontend Next.js 16 (App Router) / TypeScript / React 19
Visualization Recharts (charts) / Leaflet + React-Leaflet (maps)
Auth NextAuth.js (Strava OAuth 2.0)
Agent Python / FastAPI / Google ADK + Gemini (covered in a future post)
Storage Cloud Storage (JSON objects only; no relational DB)
Deployment Cloud Run (Web and Agent deployed as separate services)

Handling Strava API rate limits

According to Strava's official docs, as of May 2026 the default rate limits are as follows, applied per application. Going over returns 429 Too Many Requests.

Category 15 min 1 day
Overall 200 req 2,000 req
Non-Upload (read-heavy) 100 req 1,000 req

The request count limits weren't really a problem in practice. The bigger constraint is Connected Athletes = 1. With that default, only one user (me) can authorize the app, so I can't easily share it with friends. To raise it, you submit a form via Strava's Developer Program — they approved my request and bumped the limit within a day or two.

Notes on development

In the end the split landed at: frontend and BFF (Next.js API Routes) in TypeScript, and the agent (LLM + domain logic) in Python (FastAPI). I write Python day-to-day at work and don't have deep TypeScript experience, but writing the web side in TypeScript wasn't really a problem this time. Two main reasons (with the caveat that this app is small):

  1. You can grok the logic by talking to the LLM. Asking "explain the logic of this chart" or "show me where this is implemented" gets me the shape of the logic and the right code to read.
  2. Tweaking physics constants and formulas is mostly language-agnostic. Editing constants like const GRAVITY = 9.81 or basic arithmetic and trig formulas doesn't really depend on the language — as long as you can find the right spot. Iterative optimization code is a different story; for that you do need to understand the implementation itself.

On the other hand, the agent (next-workout suggestion and adjustment, covered separately) leans on the Python LLM ecosystem and my own familiarity, so I kept it separate. I did briefly experiment with using Python for the web backend, but the integration with the frontend created more debugging overhead, so I settled on "TypeScript for the UI-adjacent layer, Python for the LLM/inference layer." Carefully written interface definitions might mitigate that friction. Either way, the right split depends on the app's size and the kind of logic involved, and I'll keep refining my heuristic here.

Source code

The source code is available here. The README walks through how to set it up yourself. It runs locally, and I've also made it deployable to Cloud Run so I can access it on the go from a phone. Use the deploy script (deploy.sh.example) as a starting point and plug in your own project ID.

PerfRide 🚴

A performance management toolkit for road cyclists, powered by the Strava API.

Simulate climbs, optimize race pacing, plan periodized training, and track your fitness — all in one app.

Features

📊 Dashboard

Connect with Strava to view your recent rides, weekly training summary, and fitness progress chart (CTL / ATL / TSB). Includes per-ride detail with heart rate zones, power profile, and elevation overlay.

🏔️ Climb Simulator

Predict climbing times based on power, weight, and real segment data. Uses physics-based simulation (air resistance, rolling resistance, drivetrain loss). Search segments by map or use your Strava starred segments.

🎯 Pace Optimizer

Calculate optimal pacing strategy for time trials based on course elevation profile. Based on the research paper "A numerical design methodology for optimal pacing strategy in the individual time trial discipline of cycling" (Sports Engineering, 2025).

📅 Training Planner

Generate periodized training plans for your target race…




Wrap-up

This post introduced PerfRide, a minimal app built on top of the Strava API. With agentic coding tools like Antigravity, even outside your home language and library ecosystem, you can put together analytics that fit your own needs without much friction. As a bonus, it can save on subscription costs for paid features. For personal projects, I think it's a strong approach — give it a try.

I'll write up the agent features ("next recommended workout" and "automated weekly plan generation") in a separate post.


This article was originally published in Japanese on Zenn.