Plataforma de Reserva de Pádel
TFG · demo publicada
El reto: impedir reservas dobles admitiendo duraciones variables (60/90 min). PostgreSQL garantiza la integridad relacional y la validación de solapamientos vive en el backend, acotada a cada pista y fecha. Autenticación JWT sin estado, con endpoints protegidos por rol.
- Tipo
- Proyecto final de grado (TFG). Funciona como demo desplegada; no está en producción.
- Mi rol
- Algoritmo de detección de conflictos, esquema PostgreSQL, autenticación JWT, middleware por roles y panel de administración.
- Equipo
- Desarrollado junto a Jose Jandula, con ramas por funcionalidad y revisión de cambios antes de cada merge.

EL PROBLEMA
Una plataforma para digitalizar las reservas de pistas de pádel: centralizar la disponibilidad, evitar solapamientos y dar al club un panel de administración configurable.
El reto principal era garantizar que dos reservas nunca se superpongan con duraciones variables (60 y 90 minutos), acotando la comprobación a una pista y una fecha concretas. Eso exigía una lógica sólida en el backend y una interfaz clara para usuarios y administradores.
LA DECISIÓN CLAVE
Comprobar solo la hora exacta de inicio deja pasar solapamientos en cuanto las duraciones varían. La validación pasó al backend: antes de confirmar, se cargan las reservas de esa pista y ese día y se comprueba si el nuevo intervalo se cruza con alguno.
Las duraciones permitidas se leen de la configuración de cada pista, así que el administrador cambia las reglas sin tocar código.
const isAvailable = (courtId, startTime) => {
// Solo verifica hora exacta, no solapamientos
return !db.getReservation(courtId, startTime)
}async function validateConflict(courtId, date, startTime, duration) {
const config = await getCourtConfig(courtId)
if (!config.allowedDurations.includes(duration))
throw new ValidationError('Duration not allowed')
const dayReservations = await db.getReservationsByCourtAndDate(courtId, date)
const endTime = addMinutes(startTime, duration)
const hasConflict = dayReservations.some(r =>
startTime < addMinutes(r.startTime, r.duration) && endTime > r.startTime
)
return { valid: !hasConflict, endTime }
}CÓMO LO CONSTRUIMOS
El orden real del trabajo, de los requisitos al control de versiones.
Requisitos y arquitectura
Usuarios con roles (usuario y administrador), reservas con duraciones configurables y validación de conflictos. Elegimos PostgreSQL para garantizar la integridad de los datos.
Modelo de datos
Tablas users, pistas, reservations y admin_config. Cada reserva guarda pista_id, start_time y duration para validar solapamientos de forma eficiente.
Autenticación y roles
JWT con middleware. Los usuarios reservan; los administradores configuran duraciones, ven el calendario y resuelven conflictos.
Validación de conflictos
La lógica central: el intervalo (start_time, start_time + duration) no puede cruzarse con ninguna reserva de la misma pista y día.
Panel de administración
Gestión de pistas, calendario completo, reglas de reserva y resolución manual de conflictos.
Frontend
Pantallas responsive, pensadas primero para móvil, para reservar, ver disponibilidad y confirmar. Todo validado por el backend antes de confirmar.
Pruebas e iteración
Casos de conflicto, duraciones límite y permisos por rol, iterando con lo que encontrábamos.
Control de versiones
Git con una rama por funcionalidad; revisábamos los cambios del otro antes de cada merge.
ARQUITECTURA
Frontend
- Componentes en React
- Diseño responsive pensado para móvil
- Validación en cliente como primera capa
- Confirmación de reservas
Backend
- Node.js y Express
- Autenticación JWT
- Middleware por roles
- Validación de conflictos
- Endpoints REST
Base de datos
- PostgreSQL (Supabase) para la integridad
- Tablas users, pistas, reservations y config
- Índices en pista_id + fecha
- Restricciones para validar datos
Seguridad
- Tokens JWT con expiración
- Roles usuario y administrador
- Validación obligatoria en backend
- Comprobación de propiedad en endpoints
CAPTURAS




RESULTADO
La plataforma centraliza las reservas y rechaza los solapamientos antes de confirmarlos. Los usuarios reservan desde cualquier dispositivo y los administradores cambian las reglas sin tocar código.
Lo que más me enseñó fue la lógica de conflictos: razonar sobre intervalos de tiempo acotados a varias dimensiones (pista y fecha) y decidir qué validación vive en el servidor.
¿ENCAJO EN TU EQUIPO?