A restaurant discovery and ordering platform — browse restaurants and chefs, filter by cuisine/price/rating/distance, view dish menus, and check out with a live order history.
Built as an Nx monorepo: a Next.js frontend, a NestJS API layer, and a Strapi CMS backing content and orders.
- Features
- Tech stack
- Architecture
- Monorepo structure
- Getting started
- Environment variables
- Available scripts
- Testing & CI
- Contributing
- Restaurant discovery — browse by tab (All / Popular / New / Open Now / Map), filter by price, rating, cuisine, and distance from your location
- Map view — restaurant markers on an interactive map (MapTiler + Leaflet), with a user-location dot and recenter button
- Search — live-filtered search across restaurants, cuisines, and chefs from the header and homepage hero
- Restaurant & dish detail — meal-time tabs (Breakfast/Lunch/Dinner), dish type icons (spicy/vegan/vegetarian), signature dishes
- Chefs — chef profiles, "Chef of the Week" highlight, modal bios, deep-linking from search results
- Cart & checkout — per-user cart (survives login/logout with conflict resolution on merge), delivery + payment form, order summary, success confirmation
- Order history — past orders with re-order and order-summary views
- Auth — register/login, JWT-based session, auto-logout on token expiry
- Infinite scroll — restaurants, chefs, and order history all paginate as you scroll (see
useInfiniteScroll)
| Layer | Tech |
|---|---|
| Frontend | Next.js 16 (App Router, Turbopack), React 19, SCSS (SMACSS) |
| Backend | NestJS 11 |
| CMS | Strapi 5 (SQLite) |
| Shared | TypeScript, Nx 22 monorepo, npm workspaces |
| Maps | MapTiler + Leaflet / react-leaflet |
| Testing | Jest, React Testing Library, Playwright (e2e) |
Frontend (Next.js, :3000)
│ fetch/post
▼
Backend (NestJS, :3333)
│ proxies + transforms
▼
Strapi CMS (:1337)
The frontend never calls Strapi directly — the NestJS backend acts as a proxy layer: it fetches from Strapi, reshapes the responses into clean REST endpoints, and is the only service that holds the Strapi admin token used for order read/write.
Backend endpoints:
GET /api/restaurants,GET /api/restaurants/:idGET /api/chefs,GET /api/chefs/:idGET /api/dishes,GET /api/dishes/:idPOST /api/auth/login,POST /api/auth/registerPOST /api/orders,GET /api/orders
apps/
frontend/ Next.js 16 app (React, Turbopack, App Router) — port 3000
frontend-e2e/ Playwright e2e tests
backend/ NestJS 11 REST API — port 3333
backend-e2e/ Jest integration tests
libs/
shared-types/ TypeScript interfaces shared between frontend & backend
ui-components/ React component library (StarRating, RestaurantCard, DishCard, ChefCard, ...)
utils/ Shared utility functions
strapi/ Strapi 5 CMS — port 1337 (separate ecosystem, own node_modules)
- Node.js 24 (the lockfile is generated with npm 11)
- npm
npm install
cd strapi && npm install && cd ..Copy the Strapi env template and fill in real secrets:
cp strapi/.env.example strapi/.envCreate apps/backend/.env with a Strapi admin API token (generate one in the Strapi admin panel after first boot — see Environment variables):
STRAPI_ADMIN_TOKEN=your-strapi-full-access-token
Create apps/frontend/.env.local with a MapTiler API key (needed for the restaurants map view):
NEXT_PUBLIC_MAPTILER_KEY=your-maptiler-key
npm run dev:all # frontend + backend + strapi togetherOr run just the app (frontend + backend) if Strapi is already running elsewhere:
npm run devThen visit:
- Frontend — http://localhost:3000
- Backend API — http://localhost:3333
- Strapi admin — http://localhost:1337/admin
On first boot, create a Strapi admin user, add some restaurants/chefs/dishes content, and generate a Full access API token under Settings → API Tokens — paste it into apps/backend/.env as STRAPI_ADMIN_TOKEN.
| Variable | Where | Default | Purpose |
|---|---|---|---|
STRAPI_URL |
backend | http://localhost:1337 |
Strapi base URL the backend proxies to |
STRAPI_ADMIN_TOKEN |
apps/backend/.env (gitignored) |
— | Strapi Full-access token; required for order read/write. Must be regenerated after any Strapi DB reset |
PORT |
backend | 3333 |
Backend listen port |
NEXT_PUBLIC_BACKEND_URL |
frontend | http://localhost:3333 |
NestJS base URL used by the frontend |
NEXT_PUBLIC_STRAPI_URL |
frontend | http://localhost:1337 |
Base URL for building Strapi image URLs |
NEXT_PUBLIC_MAPTILER_KEY |
apps/frontend/.env.local (gitignored) |
— | MapTiler API key for map tiles |
All tasks run through Nx — prefer these over calling the underlying tooling directly.
npx nx run-many -t lint test build --exclude strapi # what CI runs
npx nx run @org/frontend:dev # frontend only
npx nx run @org/backend:serve # backend only
npx nx e2e @org/frontend-e2e # Playwright e2e
npx nx affected -t test # only what changed.github/workflows/ci.yml runs on every push to main and every PR:
npm ci(Node 24)npx nx run-many -t lint test build --exclude strapi
Strapi is excluded from CI since it's a separate ecosystem with its own dependency tree.
mainis protected — all changes go through a PR and need approval from a CODEOWNERS reviewer- Nx Cloud is intentionally disabled for this repo
- See
CLAUDE.mdfor detailed conventions (SCSS architecture, component patterns, known gotchas) if you're using an AI coding assistant on this repo
