Skip to content

A ride-hailing price comparison app that aggregates fare estimates from multiple providers

License

Notifications You must be signed in to change notification settings

ignaciolinari/FairFare

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

FairFare

CI License: MIT

Ride price comparison app for Buenos Aires. Compare Uber, Cabify, and DiDi in one place.

FairFare Demo

📸 Screenshots
Landing Page Apps Connected Autocomplete
Price Comparison (full results)
Sorted by Time (full results)

Why FairFare?

In Buenos Aires, ride prices vary wildly between apps depending on demand, time of day, and promotions. Some cities have ride options integrated into Google Maps or third-party aggregators, but that doesn't exist here. So I built one.

FairFare lets you compare Uber, Cabify, and DiDi side by side. Uber is the easiest to scrape since it has a web app—it's stable and serves as the reliable fallback. Cabify and DiDi required a more laborious approach: since they only offer mobile apps, I had to automate an Android emulator via Appium. It's not elegant, but it works and proves the concept.

Warning

This project scrapes third-party services (Uber, Cabify, DiDi) for personal use. The maintainers are not affiliated with these companies and are not responsible for any terms of service violations. Use at your own risk.

Features

  • Multi-provider comparison: Uber (web), Cabify/DiDi (Android emulator)
  • ARS pricing: normalized to Argentine pesos
  • Sorting: cheapest vs fastest
  • Deep links: open the provider app (or web fallback)
  • Resilience tooling: per-provider status + timeouts + scraper artifacts (for debugging)

Architecture (high level)

  • Frontend (src/): React + Vite app that calls the API and renders results.
  • Backend (server/): Express API that:
    • geocodes addresses server-side (Nominatim + cache + throttle)
    • scrapes providers:
      • Uber: Puppeteer against m.uber.com (requires login)
      • Cabify/DiDi: Appium/WebDriverIO against an Android emulator (requires being logged in on the emulator)
  • Shared types (shared/): used by both frontend and backend to avoid contract drift.

Requirements

  • Node.js 20+ and npm
  • macOS/Linux (Windows is untested)
  • Puppeteer downloads Chromium on install (or set PUPPETEER_SKIP_DOWNLOAD=1 and provide a Chrome binary)
  • For Cabify/DiDi: an Android emulator + Appium running locally (see “Debugging scrapers”)

Quick Start Checklist

Before testing the full app, make sure all services are running:

Service Command / How to Start Port Required For
Backend npm run dev --prefix server 3001 All features
Frontend npm run dev 5173 Web UI
Android Emulator Start from Android Studio — Cabify, DiDi
Appium Server appium 4723 Cabify, DiDi

Tip

Uber only? You just need Backend + Frontend.

Full comparison (Uber + Cabify + DiDi)? You need all four services running.

Verification

  1. Open http://localhost:5173 — you should see the FairFare UI
  2. Check http://localhost:3001/api/health — should return { status: "ok" }
  3. Click "Verificar conexiĂłn" (Verify connection) in the UI to confirm provider connections

Development

# Install dependencies
npm install
npm install --prefix server

# Configure environment variables (optional)
cp .env.example .env
cp server/.env.example server/.env

# Run in development (two terminals)
# Terminal 1: API
npm run dev --prefix server

# Terminal 2: Web
npm run dev

# Or run both with one command
npm run dev:all

# Run tests
npm run test        # watch mode
npm run test:run    # single run
npm run test:coverage

# Build for production
npm run build

API (backend)

Base URL: http://localhost:3001 (default)

  • GET /api/health: health check
  • GET /api/geocode?q=...: geocode an address (server cached + throttled)
  • GET /api/places?q=...: address autocomplete (server cached + throttled)
  • GET /api/auth/status: returns { isLoggedIn, hasCookies } for Uber
  • POST /api/auth/verify-apps: verifies Cabify/DiDi login status on the emulator (Appium; expensive)
  • POST /api/auth/uber/login: interactive login to generate cookies (rate-limited)
  • POST /api/auth/uber/refresh: refresh Uber session from cookies (rate-limited)
  • POST /api/rides: fetch ride options
    • request body: { origin: { address, lat, lng }, destination: { address, lat, lng } }
    • response: { options, meta } where meta includes:
      • needsLoginProviders?: Provider[] (e.g. ["uber"] or ["cabify"])
      • providerStatus?: ProviderStatus[] (per-provider outcome, duration, error/reason)

Sessions & cookies

  • The frontend sends a session header: x-fairfare-session.
  • Single-user mode (default): session id is "default" so Uber cookies are stored at:
    • server/uber_cookies.json (gitignored)
  • Multi-user mode (optional): enable VITE_MULTI_USER_SESSIONS=true (or set VITE_SESSION_ID=...), and Uber cookies are stored under:
    • server/.sessions/<hash>/uber_cookies.json (gitignored)

Environment variables

See:

Debugging scrapers

See:

Deployment notes

If you plan to run the backend beyond localhost, read:

Stack

  • React 19 + TypeScript
  • Vite 7
  • Express + Puppeteer + WebDriverIO (Appium)
  • Pino logging
  • Leaflet + OpenStreetMap (maps)

Note

Maps use Leaflet/OpenStreetMap to keep things open source and simple. If you prefer Google Maps, swapping it in is straightforward. Just bring your own API key.

Security / privacy

  • Do not commit:
    • server/uber_cookies.json
    • server/.sessions/
    • server/.artifacts/
    • any .env files

Contributing

See CONTRIBUTING.md.

Language

The UI is in Spanish (for Buenos Aires users). Documentation is in English.

License

MIT. See LICENSE.

About

A ride-hailing price comparison app that aggregates fare estimates from multiple providers

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages