Marqués — Sistema de Reservas

live

Sistema de reservas full-stack para un restaurante: API REST (Node, Express, PostgreSQL) y frontend Next.js desplegados por separado y comunicándose en producción.

Full-stack

por qué existe

Contexto

Sistema de reservas real para un restaurante: comprobar disponibilidad de mesa sin dobles reservas, gestionar el ciclo de vida de una reserva (de pendiente a confirmada o cancelada) y permitir reseñas solo a quien realmente tuvo una reserva confirmada — con un backend y un frontend que se despliegan y evolucionan por separado.

lo más difícil

Reto técnico

El manejo de errores está centralizado en un único middleware (errorHandler) que traduce excepciones de negocio (AppError) y códigos nativos de Postgres (23505 duplicado, 23503 FK inválida) a respuestas HTTP consistentes, evitando try/catch repetido en cada controlador (patrón asyncHandler). En producción, el backend en Fly.io "duerme" tras un rato de inactividad: la primera petición tras la inactividad puede tardar varios segundos en responder (cold start) mientras la máquina arranca. La mitigación es un workflow de GitHub Actions (keepalive.yml) que hace ping periódico a /health — el mismo endpoint pensado originalmente para probes de un orquestador, reutilizado para mantener la máquina despierta.

el porqué, no solo el qué

Decisiones técnicas

01

Disponibilidad garantizada a nivel de base de datos, no solo de aplicación

La restricción UNIQUE(table_id, date, time) en PostgreSQL evita reservas duplicadas de la misma mesa en el mismo turno a nivel de base de datos, además de la comprobación de disponibilidad en la capa de aplicación — la garantía real vive en la BD, no solo en el código.

02

Autorización por rol a nivel de middleware, no de controlador

Las reglas de acceso (customer / admin) quedan explícitas en la definición de las rutas en vez de esparcidas dentro de cada handler, así son visibles de un vistazo.

03

El frontend no tiene lógica de negocio propia

restaurant-web no tiene base de datos ni reglas propias: toda la comunicación con el backend pasa por una única variable de entorno (NEXT_PUBLIC_API_URL) y un cliente fetch propio, sin proxy ni API routes intermedias — separación limpia entre dos despliegues independientes.

dónde está hoy

Resultado

con qué está construido

Stack

Next.js 14 (App Router) + React 18

frontend

Encaja con un sitio mayormente de contenido (home, carta) más unas pocas rutas autenticadas (reservas, panel de administración).

TypeScript

tooling

Tipos compartidos (User, Reservation, Table, Review) entre las llamadas a la API y la UI.

Tailwind CSS

frontend

Estilado rápido para un sitio con bastante superficie (home editorial, carta, panel admin) sin mantener una hoja de estilos propia.

Node.js + Express 5

backend

API REST clásica; no hace falta un framework full-stack cuando el cliente ya es un proyecto Next.js separado.

PostgreSQL

backend

Relaciones reales entre reservas, mesas y reseñas (FKs, UNIQUE constraints) que encajan mejor en un modelo relacional que en un documento.

JWT + bcryptjs

backend

Autenticación stateless entre dos servicios desplegados por separado (Vercel / Fly.io), sin sesión de servidor compartida.

Fly.io

infra

Despliegue del backend con máquinas que se apagan en inactividad: barato para un proyecto de portfolio, a cambio de cold starts.

Vercel

infra

Despliegue del frontend Next.js con CI/CD nativo desde main.

GitHub Actions

tooling

CI (lint, typecheck, tests) en la API, y un workflow de keepalive que hace ping a /health para mitigar el cold start de Fly.io.

pruébalo tú

Demo en vivo

comprobando…
restaurant-web-lilac.vercel.appAbrir en pestaña

La web es instantánea, pero la API vive en Fly.io y duerme por inactividad: la primera petición tras un rato puede tardar unos segundos mientras arranca la máquina.

Ver el resto de proyectos