Personal / Client · 2025
Single Resto
Ordering site and dashboard for a restaurant
Restaurant platform with a public ordering site and an admin dashboard, self hosted with Docker on a VPS and taking real orders through MercadoPago.
Overview
Single Resto is a restaurant system in two parts: a public ordering site for diners and an admin dashboard for whoever runs the place. It started as a client job and ended as a white label template that serves several brands from one codebase, deployed on a VPS I administer myself.
The problem
The client took orders over WhatsApp and wrote them down by hand. Two things broke constantly: orders arriving after closing time, and orders lost between the message and the kitchen. There was no catalogue anywhere either, so prices lived in a photo of a printed menu.
And once the first restaurant worked, the next one asked for the same thing with its own brand. Rewriting the project per client was not an option.
Decisions
- Server Sent Events instead of WebSockets for the order panel: the flow is one directional, the server pushes new orders to the dashboard, and SSE gives that with a fraction of the moving parts and reconnection handled by the browser.
- Opening hours enforced on checkout, not just displayed: the site blocks the order outside business hours and says why, because the real cost was the order that arrives with the kitchen closed.
- MercadoPago as the gateway: it is what customers in Argentina already have, and its confirmation closes the order automatically instead of requiring a manual check.
- Drizzle ORM with PostgreSQL and a monorepo shared by the site and the dashboard: products, categories and store settings are one schema and one set of types, so the public menu can never drift from what the dashboard edited.
- White label from the start: themes, logos, catalogues and settings per tenant over the same codebase, each with its own URL. A new client is a configuration, not a fork.
- Self hosted on an Oracle Cloud VPS with Docker Compose: app container, PostgreSQL container, a self hosted WhatsApp API container and Nginx as reverse proxy for SSL and routing. Stabilizing the WhatsApp service was the hardest part, and it is what lets the dashboard notify customers on its own.
- Cloudflare R2 for product images: object storage behind a CDN instead of files on the VPS disk, so images survive redeploys and load fast.
- A maintenance flag in the settings: the owner can take the site offline instantly without calling me.
- Structured logging with Wide Events (canonical log lines) instead of scattered console.log: one event per action with full context, which is what later makes it possible to ask for the error rate per action.
Full ordering flow
Checkout and alerts
Admin dashboard
The dashboard gives the owner control over the whole operation:
- Order panel: incoming orders appear in near real time via SSE and move through accepted, in preparation and completed, with ticket printing support.
- Product CRUD: create, edit and delete products with image uploads to R2, category assignment, pricing and visibility toggles.
- Store configuration: opening hours, delivery zones, address and maintenance mode.
- WhatsApp actions: quick buttons that open pre formatted messages to the customer with order updates.
Order management
Product management
Image uploads
Store settings
White label demo
Maintenance mode
Example: structured Wide Event
{
"timestamp": "2026-02-19T16:40:29.786Z",
"request_id": "mltot5y2-3pf6a",
"service": "single-resto",
"environment": "development",
"action_name": "updateProduct",
"duration_ms": 23,
"outcome": "error",
"user": {
"id": "LE58YKCC",
"role": "admin"
},
"error": {
"type": "ValidationError",
"code": "VALIDATION_ERROR",
"message": "Error de validación. Revise los campos."
}
}What the logging bought
- One event per action with full context (user, duration, outcome).
- Automatic detection of errors and results using byethrow.
- Analytical queries: error rate by action, average response time by tenant.
- Full traceability with a unique request_id per request.
- Much less log noise than scattered console.log.
Result
The platform runs in production on the VPS, taking paid orders, printing tickets and notifying customers over WhatsApp, with the same codebase serving more than one brand.
This was the project that pushed me furthest from typical frontend work: multi container Docker, networking between containers, SSL, uptime, a payment gateway and production storage. It is also where I first applied, on a live system with real customers, the planning and organization I had only studied in software engineering courses.