Inspiration

Someone wanted to start running, but he lives around several hills. Every route he tried turned into a hike within minutes, which made it hard to build the habit. We wanted something closer to running on a track: a flat, steady loop that's easy to start with. So we built Desire Path, which finds loops near you with as much elevation as you desire. The name comes from the worn paths people carve across a lawn because they're the way people actually want to go.

What it does

Desire Path generates running loops that start from your postal code or your GPS location.

  • You select a target distance or time and pace, then a elevation range (minimum, maximum and average).
  • It returns up to three loops labelled easy, medium and hard, sorted by climb per km. The easy loop is the flattest and the hard loop is the hilliest.
  • Each loop comes with its distance, elevation gain and loss, an estimated time, and an elevation profile chart. You see it on a map.
  • You can start a run and track it live on the map. Your run's statistics are saved with duration, distance and elevation gain, and you can favourite them.
  • Accounts let you keep a history of your runs and statistics.

How we built it

Frontend:

  • React 19 and TypeScript, built with Vite.
  • Leaflet and react-leaflet for the route maps and live run tracking.
  • React Router for the pages.
  • Deployed on Vercel, which proxies /api to our backend.

Backend:

  • FastAPI (Python) on Render.
  • Postgres on Tiger Data stores users, saved routes and recorded runs, including the GPS trail as JSONB
  • Passwords are hashed with bcrypt, and sessions use JWTs.

Route generation:

  • A postal code is first looked up with Nominatim (OpenStreetMap). If that has no match, it falls back to the OpenRouteService geocoder.
  • We ask OpenRouteService for round-trip walking loops with elevation data, using different random seeds to get a spread of candidate loops.
  • Each candidate is scored on how it fits your elevation window, average elevation, target length, and if it overshoots your time. The best distinct ones are picked and labelled.
  • To stay inside the free API limits we added rate limiting (per IP, per user and global), an in-memory response cache where nearby postal codes share routes, and deduplication of near-identical loops.

Challenges we ran into

  • Network problems: Vercel proxies to Render, and the proxy setup took fixing. Your commit "Point Vercel /api rewrite at Render backend" was one of those fixes. Render's free tier also sleeps, so the first request after a pause is slow.
  • Sign-in problems: getting auth working across the frontend, the backend and the database, plus a forgot-password flow that still doesn't work.
  • Free-tier API limits: one generate costs about 9 ORS requests, and the free tier allows about 2000 a day. That forced us to add rate limiting and a cache. We also hit "Quota exceeded" in testing, and ORS reports it as a 403, which looked like a bad API key until we handled it.
  • Postal code accuracy: Nominatim had no entry for some codes, such as V5V 4N2. The fallback geocoder then returns a point that can be off, so we label it as approximate.
  • Staying under limits while testing: random postal codes could drain the quota, so we added a lookup limit.
  • merging conflicts as well

Accomplishments that we're proud of

  • A working end-to-end app, deployed: React frontend on Vercel, FastAPI backend on Render and a Postgres database. Your commit history shows this.
  • The route scoring: it picks the loops that fit your elevation window and length best and labels them easy, medium or hard by climb per kilometre.
  • A backend with rate limiting, caching and clear error handling, with 94 passing tests.
  • Live run tracking on a map, saved run history and favourites.
  • Solving the problem that inspired it: finding a hill-free loop in a hilly area.
  • Building it in a hackathon timeframe.

What we learned

  • Free APIs shape the design. Caching, rate limits and request counting weren't extras. They decided whether the app could work at all.
    • Error messages matter. A quota error that reads as "bad key" costs hours of debugging, so map errors to the right cause.
    • Geocoding is messy. Different services disagree, and results can be off, so it's worth showing users when a location is approximate.
    • Scoring beats a single request. Getting good routes meant generating several options and ranking them, because the API couldn't filter for flatness.
    • Deploying across services is its own work. Hosting the frontend, backend and database on different platforms meant dealing with proxies, environment variables and trusted proxy headers. We set TRUSTED_PROXY_HOPS so rate limits count the real client IP.
    • Working as a team on one repo. Branches, merges and conflicts.

What's next for Desire Path

  • Smarter loops that bias toward flat terrain instead of random selections from a pool.
  • Implement password reset as it doesn't fully work yet, and shared/public routes.
  • Arrow indicator for directions.
  • Better design for avoiding water when creating routes.
  • Allow user's to input their weight for more accurate analytics
Share this project:

Updates

Submission history