A full-stack food-delivery platform built with the Next.js App Router, React Server Components, Prisma & PostgreSQL.
Restaurant discovery, live menus, a persistent cart, authentication, and a complete checkout flow — built as a production-minded portfolio project.
MealMover is a food-delivery web app that lets a guest browse restaurants, filter and search a live menu imported from a public food API, build a cart that survives page reloads, sign in with Google, GitHub or email, and move through a full checkout and order-history flow.
The goal of the project is to demonstrate real production patterns — server-first data fetching with React Server Components, type safety from the database schema all the way to the form, URL-driven filter state, and a clean feature-based architecture — rather than a UI-only demo.
| Homepage | Restaurants |
|---|---|
![]() |
![]() |
| Restaurant detail | Cart drawer |
![]() |
![]() |
| Checkout | Transaction history |
![]() |
![]() |
| Blog | Mobile menu |
![]() |
![]() |
- Sign in with Google, GitHub, or email + password via NextAuth v5.
- Passwords hashed with bcrypt; sessions available inside Server Components.
- Protected routes (
/checkout,/transactions,/profile) guarded by middleware with return-to-page redirect after login. - Avatar and profile dropdown in the header when authenticated.
- Restaurant catalog rendered on the server (React Server Components) from the database.
- Menu items imported once from TheMealDB into PostgreSQL, so filtering, sorting and pagination happen in the database rather than against a rate-limited external API.
- Restaurant detail page with menu sections, stats panel (rating / distance / time / price) and a location map.
- Search endpoint (
/api/search) with debounced queries.
- Guest cart on Zustand persisted to
localStorage— survives reloads. - Slide-in cart drawer with quantity stepper, per-item selection, and live totals.
- Checkout page with delivery addresses, order summary and payment-method selection.
- Transaction history with History / Ongoing / Draft tabs.
- Fly-to-cart animation and an animated cart badge for tactile feedback.
- Blog — list, detail and comments, with data sourced from a public API, validated with Zod and cached in Upstash Redis.
- About and Contact pages, with a contact form and an embedded map.
- Custom 404 page.
- Animated, semi-transparent mobile menu (Motion) that layers over the page with a blurred backdrop.
- Smooth scrolling (Lenis) and scroll-reveal / hero animations (GSAP + ScrollTrigger).
- Sticky header, toast notifications, skeleton loading states.
- Fully responsive from mobile to desktop;
prefers-reduced-motionrespected.
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, RSC, Server Actions, Route Handlers) |
| UI | React 19, Tailwind CSS v4, clsx + tailwind-merge (cn() helper) |
| Language | TypeScript (strict) |
| Database | PostgreSQL (Neon) + Prisma 7 |
| Auth | NextAuth v5 + Prisma adapter + bcryptjs |
| Validation | Zod 4 (shared client/server schemas) |
| Forms | React Hook Form + Zod resolvers |
| Client state | Zustand (cart, UI) |
| Server state | TanStack Query (infinite scroll, search) |
| Virtualization | TanStack Virtual (long review / order lists) |
| URL state | nuqs (filters in the URL) |
| Animation | Motion, GSAP + ScrollTrigger, Lenis |
| 3D | Three.js + React Three Fiber + drei |
| Maps | Mapbox GL / react-map-gl |
| Caching / rate limit | Upstash Redis + Ratelimit |
| Resend + React Email | |
| Notifications | react-hot-toast |
| Testing | Vitest + Testing Library, Playwright |
The app follows a feature-based structure. Server-only data access lives in features/*/queries.ts and mutations in features/*/actions.ts, keeping the boundary between server and client explicit.
src/
├─ app/
│ ├─ api/ route handlers: auth, search, meals, orders, reviews, ...
│ ├─ about/ blog/ restaurants/ checkout/ transactions/ contact/ sign-in/ sign-up/
│ ├─ layout.tsx · not-found.tsx · globals.css
├─ components/ ui/ · layout/ · home/ · restaurants/ · blog/ · cart/ · ...
├─ features/ cart/ · orders/ · reviews/ · blog/ · auth/ (queries · actions · schema)
├─ lib/ prisma · auth · redis · email · utils
├─ hooks/ stores/ emails/ types/
prisma/ ├─ schema.prisma └─ seed.ts
Why these choices
- RSC for the catalog and menu — data is fetched on the server, so the client ships less JavaScript and gets a better LCP. Interactivity (cart, filters, map) stays client-side.
- nuqs for filters — filter state lives in the URL, so links are shareable and the back button works.
- Zustand over Context for the cart — the cart updates frequently; Context would re-render the whole tree.
- Menu data imported into the DB — a public food API can't do price filters, joins or pagination, so meals are imported once and owned locally.
- Server-side money — order totals are computed on the server from database values, never trusted from the client.
- Node.js 20+
- A PostgreSQL database (a free Neon project works)
- Accounts (all free tiers) for the services you want to enable: Google & GitHub OAuth, Upstash, Resend, Mapbox
git clone https://github.com/Metenchuk/mealmover.git
cd mealmover
npm installCreate a .env file in the project root:
# Database (Neon)
DATABASE_URL="postgresql://..."
DIRECT_URL="postgresql://..."
# Auth
AUTH_SECRET="run: npx auth secret"
AUTH_URL="http://localhost:3000"
GOOGLE_CLIENT_ID=""
GOOGLE_CLIENT_SECRET=""
GITHUB_CLIENT_ID=""
GITHUB_CLIENT_SECRET=""
# Upstash Redis (blog cache + rate limiting)
UPSTASH_REDIS_REST_URL=""
UPSTASH_REDIS_REST_TOKEN=""
# Email (Resend)
RESEND_API_KEY=""
# Maps (Mapbox)
NEXT_PUBLIC_MAPBOX_TOKEN=""npm run db:push # apply the Prisma schema
npx tsx prisma/seed.ts # seed restaurants, menu, users, content
npm run db:studio # (optional) inspect the datanpm run devOpen http://localhost:3000.
Demo account
email: demo@mealmover.dev
password: password123
| Command | Description |
|---|---|
npm run dev |
Start the dev server (Turbopack) |
npm run build |
Production build |
npm run start |
Run the production build |
npm run lint |
Lint with ESLint |
npm run test |
Unit / component tests (Vitest) |
npm run test:e2e |
End-to-end tests (Playwright) |
npm run db:push |
Push the Prisma schema to the database |
npm run db:studio |
Open Prisma Studio |
The app deploys to Vercel:
- Push the repository to GitHub and import it into Vercel.
- Add every environment variable from the
.envexample above, settingAUTH_URLto the production domain. - Add the production callback URLs to your Google and GitHub OAuth apps:
https://mealmover-5va8.vercel.app/api/auth/callback/googleand.../github. postinstallrunsprisma generateautomatically during the build.- Run the seed once against the production database.
Planned additions, in priority order:
- Stripe checkout — Checkout Session + webhook as the source of truth for payment (the
Ordermodel already carries astripeSessionId). - Realtime order tracking — Pusher channels for live status updates and a courier marker moving on the map.
- AI menu assistant — a streaming assistant that searches the menu in the database and returns dish cards.
- Test suite — Vitest unit tests for store logic, wired into GitHub Actions CI.
- i18n & PWA — English / Ukrainian locales and an installable offline experience.
Released under the MIT License.







