Yum Backend API
Yum is the central REST API for the SMACKZ platform. It handles menus, orders, authentication, pages, loyalty, image uploads, and all data mutations. Every client application (Admin, Websites, Mobile, KDS) communicates with Yum.
Tech Stack
| Component | Technology |
|---|---|
| Runtime | Node.js (>= 20.18.1, recommended 22.x LTS) |
| Framework | Fastify v5 |
| Language | TypeScript |
| Database | PostgreSQL 14 via Drizzle ORM |
| Validation | Zod |
| Auth | Firebase Admin SDK + JWT |
| Resend.com | |
| Images | Cloudflare R2 (direct upload via signed URLs) |
| Cache | Redis (ioredis) -- optional, degrades gracefully |
| Docs | Swagger UI at /docs |
Project Structure
api/
├── controllers/ # Request handlers
├── db/models/ # Drizzle ORM entities (database tables)
├── decorators/ # Firebase auth decorator
├── routes/ # API endpoint definitions
├── schema/ # Zod validation schemas
├── services/ # Business logic
├── repositories/ # Data access layer
├── utils/ # Helpers (time, email, images)
├── lib/ # Shared libraries (Redis, etc.)
├── plugin/ # Fastify plugins (Swagger, auth)
├── scripts/ # Admin & utility scripts
└── index.ts # Entry point
Getting Started
git clone <repo-url> && cd yum
npm install
cp .env.dev .env
npm run dev
The server starts at http://localhost:8080 with hot reload. Swagger UI is available at http://localhost:8080/docs.
Common Commands
| Command | Purpose |
|---|---|
npm run dev |
Start dev server with hot reload |
npm run build |
Build TypeScript to dist/ |
npm run ts.check |
TypeScript type check |
npm run lint:check |
ESLint check |
npm test |
Run test suite (Jest) |
npm run db:push |
Sync Drizzle models to database |
npm run db:studio |
Open Drizzle Studio (DB browser) |
npm run admin:menu |
Interactive admin tools menu |
Key API Domains
| Domain | Base Path | Description |
|---|---|---|
| Auth | /api/v1/auth/ |
Login, register, Firebase config, token refresh |
| Restaurants | /api/v1/restaurants/ |
CRUD, working hours, settings |
| Menus | /api/v1/restaurants/{id}/menus/ |
Menus, sections, items, serving sizes |
| Orders | /api/v1/restaurants/{id}/orders/ |
Order lifecycle |
| Pages | /api/v1/restaurants/{id}/pages/ |
Dynamic pages and sections |
| Images | /api/v1/restaurants/{id}/images/ |
Signed upload URLs, confirmation |
| Cart | /api/v1/users/{id}/restaurants/{id}/cart/ |
Shopping cart |
| Loyalty | External smackz-loyalty service |
Points, offers, tiers |
Authentication
Yum supports two auth modes:
- Web (Admin) -- Email/password via Firebase Identity Platform.
login_source: webheader always uses platform Firebase. - Mobile -- Phone OTP via Firebase Phone Auth. Each restaurant can have its own Firebase project.
The loginSource === "web" check ensures SuperAdmin users always authenticate against platform Firebase, regardless of which restaurant they are managing.
Dynamic Pages
Restaurants have configurable pages (homepage, about, contact) with ordered sections. Each section has a content type (text, chefs_favorites, featured_menu, reviews, etc.) and a view type (grid, list, carousel, row). Page and section properties are stored as JSONB, so adding new display properties never requires database migrations.
Image Pipeline
Images are uploaded directly to Cloudflare R2 via signed URLs. The flow is: generate signed URL, client uploads directly to R2, then confirms the upload with Yum. This keeps image traffic off the API server. Images can optionally be processed through Cloudflare Images for WebP/AVIF conversion.
CDN Caching
Public read-only GET endpoints are cached at Cloudflare's edge (PoPs). The Fastify onSend hook in api/plugin/cache.ts sets Cache-Control: public, max-age=60, s-maxage=3600, stale-while-revalidate=30 for matching routes; mutations trigger fire-and-forget purge calls via the Cloudflare Purge-by-URL API so the next request fetches fresh data.
| Bucket | TTL | Examples |
|---|---|---|
| Global assets | 24h | fonts, themes, layouts |
| Restaurant static | 1h | menus, items, pages |
| Computed | 5min | crowd favorites, pickup availability |
Purge scope is minimal -- editing a menu item only purges menus + menu-items + mapped-menu-items URLs. Configuration in api/utils/cacheConfig.ts (28 regex patterns); purge wrapper in cachePurgeHelper.ts. Disabled in local dev when CLOUDFLARE_API_TOKEN is absent. See yum/docs/CLOUDFLARE_CACHING.md.
Pre-commit Coverage Gate
.husky/pre-commit runs four checks in order: npm run lint:check, npm run ts.check, npm run build, then scripts/precommit-coverage-gate.js. The coverage gate enforces 100% on staged source files (lines, branches, functions, statements) measured against the unit test suite only. It lists staged files (git diff --cached --diff-filter=ACMR), classifies them, resolves related unit tests via Jest's static graph + filename convention, and runs them with testcontainers disabled. Typical wall-clock: <10s for one staged file, ~20--30s for five. See yum/docs/PRECOMMIT_COVERAGE.md.
Location-Scoped Settings
As of 2026-04, several per-branch settings (diningOptions, parkingOptions, deliveryProvider, working hours, holidays, token counters) live on locations rather than restaurants. Routes that target a single entity (CRUD-by-ID) do not include locationId; only list/query routes do. See Restaurants & Locations and yum/docs/LOCATION_SCOPED_SETTINGS.md.
Fastify v5 Async Handler Discipline
Fastify v5 expects async route handlers to return reply after reply.send(...). Omitting the return causes a "double-send" race where Fastify calls reply.send(undefined) after the handler resolves, crashing the server with ERR_HTTP_HEADERS_SENT on subsequent requests. The first request often masks the bug because the Zod serializer compilation is slow enough to write headers before the race window. Always return reply.send(...) (or return reply.status(n).send(...)) in async handlers. See yum/docs/FASTIFY_V5_DOUBLE_SEND_ISSUE.md.
Export/Import
Yum includes services for exporting and importing restaurant data:
menuExportImport-- Menus, sections, items, and mappingsrestaurantExportImport-- Restaurant configurationuserExportImport-- User accountsorderExportImport-- Order historyunifiedExport-- Combined export
Key Files
yum/CLAUDE.md-- Detailed backend guidelines and doc indexyum/api/routes/-- All route definitionsyum/api/db/models/-- Drizzle ORM table definitionsyum/api/schema/-- Zod validation schemasyum/drizzle.config.ts-- Drizzle configurationyum/docs/DATABASE_SCHEMA.md-- Full database schema referenceyum/docs/NOTIFICATIONS.md/NOTIFICATION_COVERAGE.md-- See Notificationsyum/docs/DELIVERY_ADMIN_GUIDE.md/DELIVERY_CONSUMER_GUIDE.md-- See Deliveryyum/docs/LOCATION_SCOPED_SETTINGS.md-- 2026-04 location migration referenceyum/docs/CLOUDFLARE_CACHING.md-- CDN caching configurationyum/docs/PRECOMMIT_COVERAGE.md-- Pre-commit coverage gateyum/docs/ROUTE_LOCATION_AUDIT.md-- Route locationId policyyum/docs/FASTIFY_V5_DOUBLE_SEND_ISSUE.md-- Fastify v5 async handler disciplineyum/docs/FRONTEND_MIGRATION_GUIDE.md-- Identity Platform + location-scoped settings frontend migration