A real-time restaurant ordering system built with Next.js, Django, and WebSockets. Customers can browse menus and place orders through multiple synced devices, while kitchen staff receive live notifications and manage orders with instant status updates.
| Revenue Details | Customer Insights | Item Performance |
|
|
|
- Real-time WebSocket synchronization - Cart updates and order notifications appear instantly across all connected devices
- Cross-device cart sync - Multiple people at the same table can add items simultaneously with live updates
- Type-safe API integration - Auto-generated TypeScript types from OpenAPI schema ensure compile-time safety
- Redis-backed persistence - Cart data persists across sessions and device switches
- JWT authentication - Secure token-based auth with role-based access (customer/staff/owner)
- Comprehensive analytics - Owner dashboard with revenue trends, customer metrics, and performance insights
- Framework: Next.js 14 (App Router)
- Language: TypeScript
- Styling: Tailwind CSS
- UI Components: Shadcn/ui
- Charts: Recharts
- API Client: openapi-fetch with type generation
- Framework: Django 5.0
- API: Django Ninja (OpenAPI)
- Real-time: Django Channels with Redis
- Database: SQLite (development) / PostgreSQL (production ready)
- Authentication: JWT tokens
- Cache: Redis
Carts are synced across devices using WebSocket connections. When a customer adds items on one device, changes appear instantly on all devices logged in with the same account.
- Customer adds items to cart
- Proceeds to checkout and selects payment method
- Order is created and sent to kitchen via WebSocket
- Kitchen staff updates status as they prepare the order
- Customer can track status in real-time
- Order completes when marked as delivered/completed
The owner dashboard provides comprehensive insights:
- Revenue tracking with historical comparisons
- Peak hours analysis for staffing decisions
- Menu performance metrics to optimize offerings
- Customer behavior patterns for retention strategies
Requires Docker with the Compose plugin.
POPULATE_DB=true docker compose up --buildOpen http://localhost.
| Option | Default | Description |
|---|---|---|
PORT |
80 |
Host port nginx listens on |
POPULATE_DB |
false |
Seed demo menu/orders/users on startup |
Demo credentials (created by populate_db):
- Kitchen Dashboard — username:
kitchen, password:kitchen - Django Admin — create a superuser for admin access:
docker compose exec backend uv run python manage.py createsuperuser
Hot-reload is enabled for both frontend (next dev) and backend (Daphne restarts on file change because the source directory is bind-mounted).
SECRET_KEY=<strong-random-key> docker compose -f docker-compose.prod.yml up --build| Variable | Required | Default | Description |
|---|---|---|---|
SECRET_KEY |
yes | — | Django secret key |
PORT |
no | 80 |
Host port |
ALLOWED_HOSTS |
no | * |
Comma-separated allowed hostnames |
DATABASE_URL |
no | SQLite on a volume | e.g. postgres://user:pass@host/db |
The prod stack builds next build + a standalone Node runner, collects Django static files, and serves media directly from nginx.
cd backend
uv sync
uv run python manage.py migrate
uv run python manage.py populate_db # optional demo data
uv run daphne -b 0.0.0.0 -p 8000 conf.asgi:applicationA local Redis instance is required for WebSocket support.
cd frontend
npm install
npm run dev # http://localhost:3000The frontend expects the backend at http://localhost:8000 by default.
Set NEXT_PUBLIC_API_URL in frontend/.env.local to override.
After changing backend schemas:
cd frontend
npm run update-api-types




