•
6 min read
NJ's Café & Restaurant
  • FastAPI
  • SQLModel
  • PostgreSQL
  • Next.js 14
  • TanStack Query
  • JWT
  • Docker

NJ’s Café & Restaurant is a full-stack restaurant service management system built from a real brief — a friend in hospitality described what a proper café management system should actually do, and this is the result.

FastAPI backend, Next.js frontend. Handles everything from table sessions and order tracking to customer history and menu administration.

Tech Stack

Backend

LayerTechnology
FrameworkFastAPI
ORMSQLModel (SQLAlchemy + Pydantic)
DatabasePostgreSQL (hosted on Neon)
MigrationsAlembic
AuthJWT via OAuth2 password flow
RuntimePython 3.14 (via uv)

Frontend

LayerTechnology
FrameworkNext.js 14 (App Router)
UIshadcn/ui + Tailwind CSS
Server StateTanStack Query (React Query)
HTTP ClientAxios

Features

Backend

  • Table & Session Management — Tables have types (indoor, rooftop, takeaway). Opening a session links a table to an optional customer and tracks all orders under it. Closing a session freezes the final bill and increments customer stats.
  • Order Lifecycle — Orders are created under a session, start as pending, and are toggled to served when delivered. Toggling to served snapshots the final total; toggling back unfreezes it.
  • Smart Order Items — Adding an item that already exists in an order (same menu_item_id + note) increments quantity instead of creating a duplicate. Prices are snapshotted at order time, so menu price changes don’t affect historical orders.
  • Menu Administration — Full CRUD for menu items, categories, and subcategories behind /admin routes.
  • Customer Tracking — Customers are identified by phone number. Each closed session increments their visit_count and adds to total_spent automatically.
  • Role-Based Access — Users are either admin or employee. Admin routes are protected separately from standard authenticated routes.
  • Paginated Session History — Table session history is available paginated for reporting and review.

Frontend

  • Order Dashboard — View and manage all active orders across tables, with TanStack Query invalidation keeping the UI in sync after every mutation without a page refresh.
  • Order Card UI — Each order displays its items, line totals, status, and served time. Items can be added in bulk via a menu modal or removed individually. Orders toggle between pending and served in one click.
  • Table Session View — Full session detail page showing all orders, a running bill total, and session controls.
  • Menu Item Modal — Categorized, filterable menu picker for adding items to an order with quantity selection.
  • Admin Controls — Menu item, category, and subcategory management behind role-protected routes.

Project Structure

backend/
├── app.py                  # FastAPI app entry point
├── routers.py              # Central router registration
├── database.py             # DB engine & session setup
├── base.py                 # SQLModel metadata base
├── alembic/                # Migration environment
│   └── versions/           # Auto-generated migration files
│
├── auth/                   # JWT auth — token generation & verification
│   ├── models/
│   ├── routers/
│   ├── services/
│   └── utils/
│
├── user/                   # User accounts & roles (admin / employee)
│   ├── models/
│   ├── routers/
│   ├── schemas/
│   └── services/
│
├── customer/               # Customer profiles, visit count, spend tracking
│   ├── models/
│   ├── routers/
│   ├── schemas/
│   └── services/
│
├── menu/                   # Menu items, categories, subcategories
│   ├── models/
│   ├── routers/
│   ├── schemas/
│   └── services/
│
└── service_flow/           # Core restaurant operations
    ├── diningtable/        # Table model (number, type, occupancy)
    │   ├── models/
    │   ├── routers/
    │   ├── schemas/
    │   └── services/
    ├── tablesession/       # Session lifecycle (open → orders → close)
    │   ├── models/
    │   ├── routers/
    │   ├── schemas/
    │   └── services/
    ├── order/              # Order model (pending / served toggle)
    │   ├── models/
    │   ├── routers/
    │   ├── schemas/
    │   └── services/
    └── orderitem/          # items in Order with price snapshots
        ├── models/
        ├── routers/
        ├── schemas/
        └── services/

Each backend module follows the same internal structure: models/, routers/, schemas/, services/ — keeping database logic, API layer, validation, and business logic clearly separated.

Data Model Overview

Customer
  └── TableSession (many)
        └── DiningTable (one)
        └── Order (many)
              └── OrderItem (many)
                    └── MenuItem (one)

Key design decisions:

  • price_at_time on OrderItem — menu prices can change freely without corrupting order history.
  • final_total on Order and final_bill on TableSession — totals are frozen on serve/close so live recalculation doesn’t alter settled records.
  • Cascade deletes — deleting a session deletes its orders; deleting an order deletes its items.
  • table_id is set to None on session close — freeing the table without deleting the session history.

Getting Started

Prerequisites

  • Python 3.12+, PostgreSQL (or a Neon serverless PostgreSQL connection), uv, and Node.js 18+

Backend

cd backend

# Install dependencies
uv sync

# Set up environment
cp .env.example .env

# Run migrations
uv run alembic upgrade head

# Start the server
uv run fastapi dev app.py

The API will be available at http://localhost:8000 Interactive docs: http://localhost:8000/docs

Frontend

cd frontend

# Install dependencies
npm install

# Set up environment
cp .env.example .env.local   # Set NEXT_PUBLIC_BACKEND_URL=http://localhost:8000

# Start the dev server
npm run dev

The frontend will be available at http://localhost:3000.

Environment Variables

Backend (backend/.env)

# Local PostgreSQL
DATABASE_URL=postgresql://user:password@localhost:5432/njs_cafe

# Or a Neon serverless connection string (recommended for cloud deployment)
# DATABASE_URL=postgresql://user:password@ep-xxxx.us-east-1.aws.neon.tech/njs_cafe?sslmode=require

JWT_SECRET_KEY=your-jwt-secret-key
ALGORITHM=HS256

Frontend (frontend/.env.local)

NEXT_PUBLIC_API_URL=http://localhost:8000

Database Migrations

cd backend

# Create a new migration after changing models
uv run alembic revision --autogenerate -m "describe your change"

# Apply migrations
uv run alembic upgrade head

# Roll back one step
uv run alembic downgrade -1