Restaurant Equis follows a classic three-tier web architecture: a React 19 single-page application (SPA) handles the presentation layer, a FastAPI service owns the business logic and data access layer, and PostgreSQL persists all restaurant state. In production these three tiers are stitched together by Nginx, which acts as the single public-facing entry point — serving pre-compiled static frontend files for every non-API route and transparently forwarding allDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/teofilobetancourt/Restaurant-Equis/llms.txt
Use this file to discover all available pages before exploring further.
/api/* requests to the FastAPI process listening on port 5000. In development, Vite’s dev server replaces Nginx for the frontend while Uvicorn runs the backend directly.
Request Flow
Every user interaction follows the same path through the system:- Browser sends an HTTP request to port 80 (production) or directly to
localhost:3000/localhost:5000(development). - Nginx inspects the request path:
- Requests for
/api/*,/api/docs, or/api/openapi.jsonare proxied tohttp://127.0.0.1:5000. - All other requests are served as static files from
/var/www/restaurantequis(the compiled Vite bundle).
- Requests for
- React SPA handles client-side routing for non-API requests. Unknown paths fall back to
index.htmlvia Nginx’stry_filesdirective, enabling SPA navigation without full-page reloads. - FastAPI receives proxied API requests, validates them with Pydantic, executes the business logic via SQLAlchemy, and returns JSON responses.
- PostgreSQL is queried by SQLAlchemy — no raw SQL in application code. The ORM translates Python model operations into optimized SQL and returns mapped objects.
Frontend
The frontend is a TypeScript React 19 SPA built and bundled with Vite 6. Styling is handled entirely by Tailwind CSS 4, loaded through the@tailwindcss/vite plugin — no separate PostCSS configuration is needed. The @vitejs/plugin-react plugin enables fast refresh during development.
Key source layout:
src/api/index.ts is the only file that talks to the backend. Every view imports from this module instead of making fetch calls directly, making the backend base URL (controlled by the VITE_API_URL environment variable) trivially swappable between local development and production.
The Vite config sets @ as a root-level path alias, allows the dev server to bind to 0.0.0.0:3000, and supports a DISABLE_HMR flag to pause file-watching during automated edits.
Backend
The backend entry point isbackend/main.py. On startup it runs a lifespan context manager that:
- Creates the
InventarioPostgreSQL schema withCREATE SCHEMA IF NOT EXISTS. - Calls
Base.metadata.create_all()to create all ORM-mapped tables (idempotent — safe to re-run). - Calls
seed_initial_data()to populate empty tables with demo records.
/api/docs and the OpenAPI JSON at /api/openapi.json.
Five routers register all business endpoints under the /api/ prefix:
| Router | File | Endpoints |
|---|---|---|
ordenes | routers/ordenes.py | Create, list, update state, delete orders |
productos | routers/productos.py | Read the dish catalogue |
inventario | routers/inventario.py | CRUD for supply stock |
proveedores | routers/proveedores.py | CRUD for the supplier directory |
reportes | routers/reportes.py | KPI summary and filtered order reports |
backend/schemas.py define Pydantic v2 models for every request body and response object. Pydantic validates all incoming JSON automatically — invalid payloads are rejected with a structured 422 error before they reach any business logic.
ORM models in backend/models.py use SQLAlchemy 2.x declarative syntax and cover all database tables (see Database below). CORS is configured via CORSMiddleware, reading the allowed origin list from the CORS_ORIGINS environment variable.
Database
PostgreSQL 15+ stores all application data across two schemas:public schema — Core restaurant operations
mesa — Dining tables
mesa — Dining tables
| Column | Type | Notes |
|---|---|---|
id_mesa | Integer | Primary key, auto-increment |
capacidad | Integer | Seating capacity |
estado | Enum | disponible, ocupada, reservada, fuera_de_servicio |
ubicacion | String(100) | Human-readable location label (e.g., “Terraza - M1”) |
plato — Menu dishes
plato — Menu dishes
cliente — Customers
cliente — Customers
| Column | Type | Notes |
|---|---|---|
cedula_cliente | String(20) | Primary key — national ID (e.g., “V-12345678”) |
nombre | String(100) | Full name |
telefono | String(20) | Contact phone |
email | String(100) | Email address (optional) |
direccion_habitual | String(255) | Usual delivery address (optional) |
pedido — Orders
pedido — Orders
| Column | Type | Notes |
|---|---|---|
num_ticket | Integer | Primary key, auto-increment |
tipo_pedido | Enum | mesa, pickup, delivery |
estado_orden | Enum | recibido, preparando, listo, entregado |
id_mesa | Integer FK | References mesa.id_mesa (nullable for pickup/delivery) |
cedula_cliente | String FK | References cliente.cedula_cliente |
direccion_envio | String(255) | Delivery address (nullable) |
fecha_creacion | DateTime | Order timestamp (UTC) |
detalle_pedido — Order line items
detalle_pedido — Order line items
| Column | Type | Notes |
|---|---|---|
num_ticket | Integer FK | Composite PK — references pedido.num_ticket |
id_plato | Integer FK | Composite PK — references plato.id_plato |
cantidad | Integer | Quantity ordered |
subtotal | Numeric(8,2) | Line total (quantity × unit price) |
factura — Invoices
factura — Invoices
| Column | Type | Notes |
|---|---|---|
num_factura | Integer | Primary key, auto-increment |
num_ticket | Integer FK | References pedido.num_ticket (unique — one invoice per order) |
fecha_emision | DateTime | Invoice timestamp (UTC) |
subtotal | Numeric(8,2) | Pre-tax amount |
impuesto | Numeric(8,2) | Tax amount |
total | Numeric(8,2) | Final total |
estado_pago | Enum | pendiente, pagado, anulado |
metodo_pago | String(30) | Payment method (optional) |
Inventario schema — Supply-chain management
Inventario.Insumos — Supply items
Inventario.Insumos — Supply items
| Column | Type | Notes |
|---|---|---|
ID_Insumos | BigInteger | Primary key, auto-increment |
Nombre_Insumo | String(100) | Unique supply item name |
Unidad_Medida | String(20) | Unit of measure (e.g., “Kg”, “Paquete”) |
Stock_Actual | Numeric(12,4) | Current stock level |
Stock_Minimo | Numeric(12,4) | Minimum stock threshold for alerts |
Punto_Reorden | Numeric(12,4) | Reorder point |
FK_IDCategoria | BigInteger FK | References Inventario.Categoria.ID_Categoria (optional) |
Inventario.Proveedores — Suppliers
Inventario.Proveedores — Suppliers
| Column | Type | Notes |
|---|---|---|
ID_Proveedor | BigInteger | Primary key, auto-increment |
Nombre_Empresa | String(150) | Company name |
Identificación_RIF | String(30) | Unique tax ID (Venezuelan RIF format) |
Ciudad | String(100) | City |
Telefono_Empresa | String(30) | Company phone |
Email_Empresa | String(100) | Unique company email |
Direccion | String(255) | Street address |
Nombre_Encargado | String(100) | Contact person’s name |
Inventario.Categoria — Supply categories
Inventario.Categoria — Supply categories
| Column | Type | Notes |
|---|---|---|
ID_Categoria | BigInteger | Primary key, auto-increment |
Nombre_Categoria | String(100) | Unique category name |
Inventario schema are created automatically by SQLAlchemy at backend startup — no migration tool or manual DDL is required for local development.
Nginx
In production, Nginx acts as the single public entry point on port 80. Its configuration (nginx.conf) implements the following routing rules:
nginx.conf (abbreviated)
try_files $uri $uri/ /index.html rule is what makes client-side routing work — any URL that doesn’t match a static file is served index.html, allowing React Router to handle the route on the client side. Compiled assets under /assets/ receive a one-year Cache-Control: immutable header because Vite injects a content hash into every asset filename.
Environment Variables
- Frontend (.env.local)
- Backend (backend/.env)
Place this file in the project root alongside
package.json.| Variable | Example Value | Description |
|---|---|---|
VITE_API_URL | http://localhost:5000 | Base URL of the FastAPI backend. Injected into the Vite bundle at build time via import.meta.env. |
VITE_WHATSAPP_NUMERO | 584140000000 | Restaurant WhatsApp number (country code + number, no symbols). Used by the POS to generate wa.me order summary links. |
.env.local