Case Study

QR-Based Restaurant Ordering App

Scan a QR at the table, browse a live menu with images & categories, place orders tied to that table, track status in real-time, and call a waiter when ready to pay. Admins manage items, images, and orders with bulk cleanup.

See how it works Tech stack
Production-ready patterns
  • Express + SQLite (schema-aware SQL)
  • Static uploads via Multer
  • React + Axios + Context
  • Socket.IO realtime events
  • QR tokens per table

How it works

1) Scan QR
Open /menu?t=< token>
2) Resolve table
Token → { table_id }
3) Browse & add
Images, categories, cart
4) Order & track
Status: pending → served

Customer flow

  • QR points to /menu?t=< token>
  • li>
  • Create items with image & category
  • Live board for status changes
  • Cleanup served/cancelled or all orders
  • Stations page lists table QR links

Key features

Menu with images & categories

Uploads via Multer → static hosting at /uploads. Category chips for quick filtering.

Table-scoped orders

Each QR embeds a token; the app resolves it to a table and scopes orders accordingly.

Order status lifecycle

PATCH /api/order/:id enforces valid transitions: pending → accepted → preparing → served.

Realtime updates

Socket.IO emits order:new and order:status so admins and customers see instant changes.

End-of-day cleanup

Bulk delete served/cancelled orders via DELETE /api/order/cleanup to keep the board tidy.

Schema-aware SQL

Detects available columns/tables with PRAGMA, avoiding “no such column” errors during incremental migrations.

Architecture & tech

Client (React)
  - Vite, Axios
  - Context cart
  - Socket.IO client
  - Image prefix: VITE_API_URL

Server (Express)
  - Routers: /menu, /order, /table, /media
  - Multer uploads → /uploads
  - Socket.IO emits: order:new, order:status, table:call

SQLite (file, single connection)
  - menu_items (image_url, category/category_id)
  - orders (table_id, item_id, quantity, status)
  - tables (id, number, token)
  - categories (optional)

Stack

React Express SQLite Multer Socket.IO Axios Vite

Security & DX

  • One DB connection; absolute paths
  • CORS configured for dev
  • Static hosting for media
  • Robust error handling & logging

API overview

Key endpoints

  • GET /api/menu
  • POST /api/menu
  • PATCH /api/menu/:id
  • POST /api/media/upload
  • POST /api/order
  • PATCH /api/order/:id
  • DELETE /api/order/cleanup?status=served,cancelled
  • GET /api/table/resolve/:token

Sample request

POST /api/order
Content-Type: application/json

{
  "table_id": 12,
  "items": [
    { "item_id": 1, "quantity": 2 },
    { "item_id": 7, "quantity": 1 }
  ]
}

UI preview

Customer: Menu & Cart

Customer menu and cart UI
Responsive menu grid with images

Admin: Orders Board

Admin orders dashboard with status controls
Live status updates + bulk cleanup

Admin: New Item

Admin create item form with image upload
Image upload → static /uploads

Highlights