What I Learned Building My Marathon App
This project aims to solve a problem that I've experienced in the past. As a runner and (soon-to-be) marathoner, there are several races I would love to run someday. For many of them, however, entrance is highly competitive (World Majors, especially). With all of these races to consider, I've also missed the registration deadlines for some of my favorites. There's nothing like logging on 6 months before the Tokyo marathon is set to happen only to realize you missed the registration deadline. Better luck next year.
I used Claude Code to assist in building a dashboard to track sign-ups for marathons. Users can log in to see 700+ marathons (more are added daily via a scraping bot), sign up for their favorites, and integrate gmail accounts for live notifications and reminders. I outlined a few learnings below:
Web scrapers breaking
The original scrapers (marathon guide, runningintheusa, worlds marathons) all returned 0 results because websites had changed structure or were blocking requests.
A better data source
I found that FindMyMarathon.com uses schema.org structured markup, which made it much more reliable to scrape. This discovery enabled our list to grow from 30 seeded marathons to 747.
API endpoints built
- Marathons API (/api/marathons)
- Scraping API (/api/scrape)
- Auth API (/api/auth)
- Reminders API (/api/reminders)
Design refresh
The initial design for the app was generic. I suggested that Claude Code match the styling of Strava, the popular running app. CC suggested a change from a basic blue color scheme to an energetic orange palette and added Jakarta Sans font.
Railway volume
While pushing changes to the app to GitHub from the CLI, I encountered errors with data inconsistency. My marathon data that was available in a prior commit had disappeared. Adding a persistent volume mounted at /data within Railway solved the data loss issue. Without this change, the SQLite database was being wiped on every deployment since the Railway containers are ephemeral.