A fullstack restaurant discovery app built with React, Node.js, and MongoDB. You give it your location, it pulls real restaurants from OpenStreetMap and ranks them based on what you actually like to eat.
- Detects your location and fetches nearby restaurants from the Overpass API (OpenStreetMap's query layer)
- Caches results in MongoDB so you're not hammering the OSM servers on every visit
- Shows restaurants on an interactive Leaflet map
- Ranks results using a weighted scoring system that accounts for cuisine match, rating, and price range
- Lets logged-in users save favourites and set preferences (cuisines, price range) that feed into the recommendation engine
- Filter and paginate through restaurants on the Explore page
Frontend — React 18, React Router v6, React Leaflet, Vite
Backend — Node.js, Express, Mongoose
Database — MongoDB Atlas (with a 2dsphere index for geo queries)
External API — Overpass API (OpenStreetMap)
Auth — JWT with bcrypt password hashing
restaurant-app/
├── backend/
│ └── src/
│ ├── config/ # MongoDB connection
│ ├── controllers/ # Route handlers (auth, restaurants, user)
│ ├── middlewares/ # JWT auth middleware
│ ├── models/ # Mongoose schemas (User, Restaurant)
│ ├── routes/ # Express routers
│ ├── services/ # Overpass fetcher, recommendation engine
│ └── server.js
└── frontend/
└── src/
├── components/ # Navbar, RestaurantCard, FilterBar, MapComponent, Loader
├── context/ # AuthContext
├── hooks/ # useGeolocation
├── pages/ # Home, Explore, RestaurantDetail, Login, Register, Profile
├── styles/ # CSS files
└── utils/ # Axios instance, helpers
You'll need Node.js, npm, and a MongoDB Atlas connection string.
git clone https://github.com/your-username/forkfinder.git
cd forkfinder/restaurant-appcd backend
npm installCreate a .env file in the backend/ directory:
PORT=5000
MONGO_URI=your_mongodb_atlas_uri
JWT_SECRET=your_secret_key
JWT_EXPIRE=7d
NODE_ENV=development
Start the server:
npm run devcd ../frontend
npm installCreate a .env file in the frontend/ directory:
VITE_API_URL=http://localhost:5000/api/v1
Start the dev server:
npm run devThe app will be running at http://localhost:5173.
The scoring formula is:
Score = (Cuisine Match × 0.5) + (Normalized Rating × 0.3) + (Price Compatibility × 0.2)
Cuisine match is binary — 1 if the restaurant's cuisine is in your preferences, 0 if not. Rating is scaled from the 1–5 range to 0–1. Price compatibility drops off gradually — an exact match scores 1.0, one tier off scores 0.6, two tiers scores 0.3.
If you haven't set any preferences yet, the engine defaults to neutral weights (0.5) across the board so the ranking still works.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/v1/auth/register |
— | Register a new user |
| POST | /api/v1/auth/login |
— | Login and get JWT |
| GET | /api/v1/restaurants |
— | List restaurants (filter + pagination) |
| GET | /api/v1/restaurants/nearby |
— | Fetch restaurants near lat/lng |
| GET | /api/v1/restaurants/recommend |
✓ | Personalised ranking for logged-in user |
| GET | /api/v1/restaurants/:id |
— | Single restaurant detail |
| POST | /api/v1/user/favorites/:id |
✓ | Toggle a favourite |
| GET | /api/v1/user/profile |
✓ | Get user profile + preferences |
Rate limiting is applied across all /api routes — 100 requests per 15 minutes.
- The Overpass API has a 10-minute cache baked in. If you hit
/nearbyfor the same area within that window, it returns from MongoDB instead of calling OSM again. - Restaurant data from OSM is inconsistent in some regions — names and addresses may be missing for less-documented areas. The app falls back to sensible defaults where data isn't available.
- The
.envfiles are not committed. Don't push your credentials.