Transforming every smartphone into a smart table kiosk via instant QR scans with real-time kitchen orchestration, live order tracking, bill splitting, and waiter assistance.
BiteFlow is a modern, full-stack digital dining and kitchen management platform designed for restaurants, bistros, bars, and cafes. By eliminating app downloads, physical menus, and ordering bottlenecks, BiteFlow allows seated diners to scan a table-specific QR code, explore a rich visual menu, customize orders, track food progress live from the kitchen stations, call for assistance, and split or settle bills directly on their phones.
Kitchen staff and floor managers get a synchronized, real-time Kitchen Display System (KDS) and floor console with station routing (Grill, Pantry, Pastry, Bar), table status monitoring, dynamic menu management, and instant waiter alerts.
For guests (/t/:table)
- Mobile-first menu with chef's picks, a sticky category bar with scroll-spy, and search
- Dish details with dietary tags and allergen information entered by the restaurant (or a clear "ask staff" notice when there isn't any)
- Cart with per-item notes, kitchen notes and server-validated promo codes
- Pay up front by card (Stripe Checkout), or run a tab and request the bill, split evenly if needed
- Live order tracker (received → preparing → ready → served) and printable receipts
- Call staff for water, cutlery or a server, with live confirmation when it's handled
For staff (/staff, PIN sign-in)
- Register (POS): product grid and order ticket for dine-in (by table) or takeaway orders, with item and order notes. Hold parks a ticket without telling the kitchen and Held orders recalls it. Send to kitchen fires it to the KDS and can print an 80mm kitchen ticket. Dine-in orders go on the table's bill, which guests also see on their phones; takeaway orders are paid from the Takeaway queue.
- Manager approval: voiding a sent order, deleting dishes or tables, regenerating QR codes, importing the sample menu and resetting demo data need a manager. A manager is signed in, or keys in their PIN on the staff member's screen, as on a real till.
- Kitchen display: New / Preparing / Ready columns, station filters (grill, pantry, pastry, bar), ticket timers that turn amber and red, and an audible chime for new tickets
- Floor: one queue for help requests, bill requests and plates ready to run, plus a live table map with open tabs
- Menu: add and edit dishes, toggle sold-out instantly, manage dietary and allergen data
- Tables & QR codes: per-table codes generated locally, PNG download, printable table tents, and code rotation to revoke a leaked link
- Reports: today's revenue, orders, average order value, time to ready, best sellers and orders by hour
Light and dark themes, keyboard and screen-reader friendly, and respectful of prefers-reduced-motion.
- Type: Changa for headings, prices and table numbers; Ubuntu for everything else. Both are self-hosted via Fontsource.
- Colour: a signal orange accent (
#D23F06) with a yellow highlight, white cards on a warm-grey ground, and black price pills. Every token is defined once insrc/index.css, with a separate dark-mode set, and text pairings are checked against WCAG AA.
npm install
npm run devOpen http://localhost:3000 and choose Try a demo table. Open http://localhost:3000/staff in another window (demo PIN 1234) to watch orders arrive live.
Development runs with demo mode on: the database is seeded with a sample menu, eight tables and a few orders in progress. Reset demo data in the staff sidebar restores it at any time.
shared/ Types, constants and zod validators used by both sides
server/
index.ts HTTP server, Vite middleware (dev) or static files (prod)
app.ts Express app: security headers, routes, error handling
realtime.ts WebSocket hub on /ws with "staff" and per-table rooms
auth.ts PIN login, hashed expiring sessions in an httpOnly cookie
db/ Drizzle schema, generated migrations, seed data
services/ Pricing, order lifecycle, tables, assistance, reports, payments
routes/ Public (diner), auth and staff routers
src/
app/ Router and providers
lib/ API client, realtime client, React Query hooks
ui/ Design-system primitives (buttons, sheets, forms…)
features/ diner/ and staff/ screens (staff is a separate lazy-loaded chunk)
Key design decisions:
- The server prices every order. Clients send item IDs and quantities only; prices, discounts and totals come from the database.
- Table QR codes carry a secret token. Diners can browse without one, but ordering, calling staff and requesting the bill all require it. Staff can rotate a table's token from the Tables screen.
- Order lifecycle is enforced server-side:
awaiting_payment → pending → preparing → ready → served → completed(orcancelled). Card orders only reach the kitchen once paid, and an order completes when it's both served and paid. - Table status is derived from open orders and bill requests, never set by hand, so it can't drift.
- Realtime updates feed React Query. The server pushes small events; the client updates or refetches the affected queries, and refetches everything after a reconnect.
- Money is integer cents end to end.
Copy .env.example to .env. Everything is optional in development.
| Variable | Default | Purpose |
|---|---|---|
APP_URL |
http://localhost:3000 |
Public URL, used in QR codes and Stripe redirects |
DATABASE_URL |
./data/biteflow.db |
SQLite file, created and migrated on start |
RESTAURANT_NAME, CURRENCY, LOCALE |
BiteFlow Bistro, USD, en-US |
Branding and money formatting |
DEMO_MODE |
on in dev, off in prod | Seeds sample activity and shows demo helpers |
STAFF_DEMO_PIN |
1234 |
4-digit PIN for the first staff account (applied when the database is first created) |
MANAGER_PIN |
none | Private 4-digit PIN for the manager account, which approves voids and destructive changes. In demo mode the public PIN is always a plain staff account |
STRIPE_SECRET_KEY |
none | Enables real Stripe Checkout. Without it, card payments use a labelled demo flow |
STRIPE_WEBHOOK_SECRET |
none | Verifies POST /api/payments/stripe/webhook |
To test Stripe locally, use a test-mode key and stripe listen --forward-to localhost:3000/api/payments/stripe/webhook. Payments are also confirmed when the guest returns from Checkout, so the webhook is optional in development.
| Command | What it does |
|---|---|
npm run dev |
Dev server with hot reload |
npm run build |
Build the client to dist/client and bundle the server to dist/server |
npm start |
Run the production build |
npm test |
API and unit tests (Vitest + Supertest, in-memory SQLite) |
npm run test:e2e |
Playwright: a diner and a staff browser through the full service loop |
npm run typecheck / npm run lint / npm run format |
Static checks |
npm run db:generate |
Generate a migration after changing server/db/schema.ts |
docker build -t biteflow .
docker run -p 3000:3000 -v biteflow-data:/data \
-e APP_URL=https://order.example.com -e STAFF_DEMO_PIN=4826 biteflowThe app is a single Node process with SQLite, so one small instance with a persistent volume is enough for a restaurant. Run it behind HTTPS: session cookies are Secure in production.
railway.json tells Railway to build the Dockerfile and health-check /api/health.
-
New project → Deploy from GitHub repo, pick this repo.
-
Add a volume to the service, mounted at
/data(the database lives at/data/biteflow.db). -
Variables:
Variable Value PORT3000(matches the domain's target port below)RAILWAY_RUN_UID0— Railway volumes are root-owned; this lets the app write to itSTAFF_DEMO_PINthe public 4-digit PIN for visitors (applied when the database is first created) MANAGER_PINyour private 4-digit PIN; the only one that can approve voids, deletions and resets APP_URLhttps://${{RAILWAY_PUBLIC_DOMAIN}}(or your custom domain)DEMO_MODEtruefor a public demo with sample orders,falsefor a real restaurantRESTAURANT_NAME,CURRENCY,LOCALEoptional STRIPE_SECRET_KEY,STRIPE_WEBHOOK_SECREToptional -
Settings → Networking → Generate domain (target port
3000). -
Keep it to one replica: SQLite and the realtime hub live in a single process.
For Stripe, add a webhook endpoint at https://<your-domain>/api/payments/stripe/webhook listening for checkout.session.completed.
- Sample allergen data is illustrative. A real restaurant must enter and verify its own.
- Sample dishes imported from TheMealDB and TheCocktailDB have no allergen data, and are flagged as such for staff and guests.
- Not yet built: multiple staff accounts and roles, customer accounts and loyalty, per-guest online bill splitting, and printer integration for kitchen tickets.
This project is licensed under the MIT License.