Fair-Share
Control de gastos compartidos con autenticación JWT, liquidación de saldos en servidor y desglose por categorías, con cliente React y API Express/MongoDB.

Problema
Repartir gastos compartidos en un grupo — alquiler, viajes, suscripciones conjuntas — suele acabar en una hoja de cálculo en la que nadie confía. Quería una herramienta que calculara automáticamente quién debe qué, sin cuentas manuales.
Enfoque
Dos repositorios: un cliente React y una API REST en Node/Express respaldada por MongoDB. Los usuarios se registran, crean grupos, registran gastos en ellos eligiendo quién pagó y entre quiénes se reparte el coste, y todos los saldos se derivan de ese libro de cuentas en lugar de introducirse a mano. Las imágenes viven en Supabase Storage, así que la API nunca gestiona subidas de archivos.
Arquitectura
- Cliente: React 19 + Vite, React Router 7 con todas las rutas
posteriores a la landing detrás de
React.lazy. Todas las peticiones pasan por una única instancia de axios configurada que adjunta el token bearer y, ante un401, lo borra y redirige al login — los componentes pasan rutas, nunca URLs - Servidor: Express 5 y Mongoose sobre MongoDB Atlas, estructurado en rutas por recurso (auth, usuarios, grupos, gastos), con hashing de contraseñas mediante bcrypt y gestión centralizada de errores
- Cálculo de saldos: un único módulo,
expenseCalculations.js, que expone los totales por usuario y los saldos por miembro del grupo — la única fuente de verdad para cada cifra que renderiza la interfaz - Almacenamiento: Supabase Storage para fotos de perfil, de grupo y de recibos
Decisiones clave
- Los cálculos de saldo viven en un solo módulo en lugar de recalcularse a demanda en cada componente, de modo que la cifra que ve un usuario en el resumen, en una tarjeta de grupo y en la cabecera del grupo es siempre la misma cifra
- La autorización se estrecha por acción en lugar de quedarse en el router:
hay rutas solo para uno mismo, otras solo para miembros y otras solo para
el autor, y cada una devuelve un
401/403/404real en lugar de responder siempre200 - Los hashes de contraseña se excluyen por defecto de todas las respuestas de la API y solo se recuperan internamente para verificar un login
- Los derechos de administrador de un grupo son transferibles, porque un grupo sobrevive a quien lo creó
Resultados
Un gestor de gastos desplegado con cuentas autenticadas por JWT y rutas
protegidas, grupos con administración transferible, selección de pagador y
reparto por gasto, saldos pagado/prestado/neto tanto individuales como de
grupo, una barra de saldo a dos tonos repetida en el resumen y en cada
tarjeta de grupo, un gráfico de anillo del gasto por categoría, subida de
imágenes, un raíl de navegación que se colapsa en un cajón en móvil, y
controles etiquetados con anillos de foco visibles y soporte de
prefers-reduced-motion.
Qué haría diferente
Ninguno de los dos repositorios tiene framework de tests, que es lo primero
que añadiría ahora que el cálculo de saldos está lo bastante centralizado
como para merecer pruebas. También normalizaría los payloads de la API:
expenseAuthor y expenseUsers llegan como cadenas de ID desde los
endpoints de listado pero como objetos poblados desde los de detalle, y
cada consumidor tiene que gestionar la forma que le toque.