[ Trabajo seleccionado ] / 2026
Broslunas Link
>>> Resumen
Páginas de enlaces multi-tenant: cada persona o negocio tiene la suya en su propio subdominio, con temas, WhatsApp y código QR.
- Rol
- Diseño y desarrollo
- Stack
- Next.js, React, TypeScript, Tailwind CSS, MongoDB, Auth.js, Cloudflare R2, Vercel
- Año
- 2026
>>> Detalles
Una alternativa a Linktree donde la dirección es tuya: marta.broslunas.link. Es un proyecto de portfolio para demostrar una arquitectura multi-tenant real, gratuito y sin pagos.
- +Subdominio propio por página: un
proxy.tsresuelve el host y reescribe a la ruta del tenant sin cambiar la URL visible. - +Aislamiento de datos: toda consulta pasa por una capa de acceso que exige
tenantId, con un test que comprueba que dos tenants nunca se cruzan. - +Editor con enlaces ordenables (arrastrar y soltar), títulos de sección, botón de WhatsApp y vídeos de YouTube o Spotify.
- +Siete temas prefabricados (Aurora, Papel, Neón, Pastel, Atardecer, Brutal, Océano) y ajuste de fuente, colores y estilo de tarjeta.
- +Código QR descargable en PNG o SVG, y dominio propio opcional.
- +Estadísticas de visitas y clics por enlace, sin cookies.
- +Caché por etiqueta de tenant, invalidada al guardar desde el panel.
- +Acceso con Google o enlace mágico por correo, y límites de frecuencia en registro y creación.
>>> Caso de estudio
- 1
- Días de desarrollo
- 24
- Commits
- 65
- Tests
- 7
- Temas
* Días y commits salen del historial de git (18/10/2026); los tests, de `pnpm test` (Vitest, 9 archivos, todos en verde).
[ 01 ] Reto
Los autónomos y los negocios locales viven de enlazar su web, sus reservas, su WhatsApp y sus redes desde un único sitio, y las herramientas típicas ponen la dirección en una ruta ajena (
dominio.com/usuario). Quería una versión donde cada persona tuviera su propio subdominio, con su tema y su QR, y que sirviera además de base para demostrar una arquitectura multi-tenant de verdad: datos aislados, dominios dinámicos y cero pagos.[ 02 ] Solución
Un
proxy.tslee el host de cada petición y reescribe internamente a/tenant/[slug]sin cambiar la URL visible;app.sirve el panel y la raíz, la landing. Todas las consultas pasan por una única capa de acceso que exigetenantId. La página pública se cachea con una etiqueta por tenant y se invalida al guardar desde el panel. Cada alta registraslug.broslunas.linken Vercel por API para que emita su certificado, y el editor permite ordenar enlaces, elegir entre siete temas, añadir WhatsApp, vídeos de YouTube o Spotify y descargar el QR en PNG o SVG.[ 03 ] Resultado
Una plataforma en producción en broslunas.link, con 65 tests (incluido el de aislamiento entre tenants), siete temas, dominio propio opcional y estadísticas de visitas y clics, construida en un solo día según el historial de git.
[ Decisiones técnicas ]
01Una capa de acceso que exige `tenantId` en cada consulta
- Alternativa descartada
Filtrar por tenant en cada consulta suelta y confiar en la revisión de código.
- Por qué
forTenant(id)es la única puerta a enlaces, tema y eventos, y toda consulta lleva eltenantIddentro. Un test crea dos tenants y comprueba que ninguna lectura ni escritura cruza datos, así que un olvido se detecta en CI y no en producción.
02Subdominios con rewrite en `proxy.ts`
- Alternativa descartada
Rutas tipo
/u/martao un despliegue por cliente.- Por qué
El proxy resuelve host → slug y reescribe a
/tenant/[slug], de modo que hay una sola app y una sola base de datos, y la dirección visible esmarta.broslunas.link. El precio es depender del DNS comodín y de registrar cada subdominio para el certificado.
03Caché por etiqueta de tenant, invalidada al guardar
- Alternativa descartada
Renderizar la página en cada visita o cachear con un tiempo fijo.
- Por qué
getPublicPageusause cachecon la etiquetatenant:<slug>. La página pública es estática hasta que su dueño guarda un cambio, y entonces solo se invalida la suya. Las visitas y clics se registran aparte, sin pasar por la caché.
04Solo `http`, `https`, `mailto` y `tel`, y embeds solo de YouTube y Spotify
- Alternativa descartada
Aceptar cualquier URL y escaparla al pintarla.
- Por qué
Un enlace
javascript:no llega a guardarse. Para losiframe, la URL del usuario nunca se usa comosrc: se convierte a la URL del reproductor de un host conocido, y cualquier otro host se descarta.
05Límite de frecuencia en MongoDB con TTL
- Alternativa descartada
Añadir Redis o Upstash solo para esto.
- Por qué
Una ventana fija con
findOneAndUpdatey un índice TTL reutiliza la base de datos que ya existe y es compartida entre instancias. Es menos fino que un limitador dedicado, pero para el registro y la creación de páginas basta y no añade infraestructura.