Bike Trip Planner
Plan your bikepacking adventures with confidence.
Paste a Komoot, Strava or RideWithGPS link, or upload a GPX file, and get a day-by-day roadbook with smart pacing, safety alerts, accommodation suggestions and weather. Take it on the road with the web app or the native mobile app, or let an AI agent plan it for you over MCP.
New here? Follow Plan your first trip. Setting up the code? Start with Getting Started.
Architecture overview
Web (Next.js 16) / Mobile (Expo) PHP backend (API Platform 5 on Symfony 8.1)
@btp/core: shared types + SSE REST API + MCP server (OAuth 2.1)
reducers GPX parsing + pacing engine
Zustand + Immer (in-memory) Valhalla routing + PostGIS reference data
openapi-fetch (typed) Async workers (Symfony Messenger)
Mercure SSE (real-time) <-- Mercure publisher, Open-Meteo weather
PostgreSQL 18 + Redis
The clients send trip requests over REST; the backend computes the stage breakdown, then
enriches it asynchronously across WORKER_REPLICAS workers and pushes updates over Mercure
SSE. PostgreSQL persists accounts, trips and stages, and holds the OpenStreetMap /
DataTourisme reference index (PostGIS); Redis holds transient computation state, the
Messenger transport and external API caches.
Type safety is enforced end-to-end: PHP DTOs define the schema -> API Platform exports an
OpenAPI spec -> npm run typegen generates TypeScript types into the shared @btp/core
package -> openapi-fetch provides type-safe API calls in both the web and mobile apps.
See Architecture for the full picture and the reasoning behind each choice.
Tech stack
| Layer | Technology |
|---|---|
| Backend | PHP 8.5, Symfony 8.1, API Platform 5.0, Doctrine ORM 3, FrankenPHP (Caddy + Mercure) |
| Data | PostgreSQL 18 + PostGIS, Redis 8, Valhalla (routing) |
| Web | Next.js 16 (App Router), React 19, TypeScript (strict), Tailwind CSS 4 |
| Mobile | Expo SDK 57, React Native 0.86, MapLibre React Native |
| State | Zustand + Immer (in-memory), Mercure SSE (real-time) |
| Testing | PHPUnit 12 (api/) and 13 (provisioner/), Playwright 1.63, Jest (mobile) |
| Quality | PHPStan level 9, PHP-CS-Fixer, Rector, ESLint, Prettier |
| Async | Symfony Messenger, Redis transport, WORKER_REPLICAS workers (2 by default) |
Explore the documentation
| Section | Page | For |
|---|---|---|
| Tutorials | Plan your first trip | Riders: from a route link to an exported roadbook |
| Getting Started | Developers: run the stack locally | |
| How-to guides | Connect an AI agent | Plug Claude or another MCP client into your account |
| Contributing | Development workflow, standards, and tooling | |
| Deployment | CI/CD pipeline, secrets, rollback | |
| Runbooks | Operations playbooks: workers, DB, Redis, Mercure, zones, releases | |
| Reference | Features | What the product does, by area |
| Supported route sources | Accepted Komoot / Strava / RideWithGPS / GPX inputs | |
| Accommodation types | Logical types, OSM tags, pricing heuristic | |
| Alert engine | Every alert code: severity, priority, trigger | |
| External data sources | OSM, DataTourisme, OpenAgenda, Wikidata, Open-Meteo, Nominatim | |
| MCP tools | The tools an AI agent can call | |
| Mobile Mercure auth | SSE authentication for the native app | |
| Claude Code tooling | MCP servers, hooks, and skills for AI-assisted development | |
| Legal & Licensing | Licence, data attribution, GDPR posture | |
| Explanation | Architecture | System overview and the reasoning behind it |
| Architecture Decision Records | Every major technical choice, with context and alternatives | |
| MCP feasibility spike | Time-boxed investigation behind the MCP server | |
| OAuth2 server feasibility spike | Time-boxed investigation behind the MCP authorization server | |
| DataTourisme flux audit | What the DataTourisme feed actually carries |