Blog
9 min de lectura

Construimos un SaaS completo de gastos corporativos en 17 sesiones de desarrollo

24.400 líneas de código. 231 commits. 14 tablas. 52 tests. 19 API endpoints. 28 páginas. Un producto listo para su primer cliente de 50 usuarios. Y lo hicimos en 17 sesiones, no en 17 meses.

SaaSDesarrollo WebNext.jsSupabaseArquitecturaCaso Real
𝕏in

Un cliente nos planteó un reto que nos encantó:

"Quiero competir con las grandes herramientas de gastos corporativos del mercado español. Tengo un primer cliente de 50 usuarios esperando. ¿En cuánto tiempo podéis tener algo funcional?"

La respuesta honesta era: depende de lo que entiendas por "funcional". Un MVP con captura de gastos, 3 semanas. Un producto completo con OCR, workflow de aprobación, exportación multi-formato, fiscalidad española, Stripe, dark mode, y RGPD — eso es otra historia.

Lo hicimos en 17 sesiones de desarrollo. No 17 meses. No 17 sprints. 17 sesiones de trabajo enfocado, con las herramientas adecuadas y las decisiones correctas desde el día 1.

17 sesiones

De la idea al producto completo con primer cliente.
24.400 líneas · 231 commits · 0 deploy failures.

Lo que entregamos

No era un prototipo. Era un producto completo, listo para facturar. Esto es lo que incluía el día que el primer cliente empezó a usarlo:

Páginas funcionales
28
dashboard, gastos, informes, aprobaciones, config...
API endpoints
19
OCR, fiscal tips, export, Stripe, push, ARCO...
Componentes React
42
layout, gastos, settings, exportación, charts...
Tests unitarios
52
fiscal, rate-limit, webhooks, RBAC, sanitización...

Además: 4 roles de usuario (owner, admin, approver, employee), 15+ permisos granulares, 4 planes de pricing con Stripe live, 3 tipos de gasto (estándar, kilometraje, dietas), exportación en XLSX/PDF/CSV con desglose fiscal, dark mode, notificaciones push nativas, cumplimiento RGPD con endpoint ARCO, y cero dependencias con vulnerabilidades.

Las decisiones que hicieron posible la velocidad

17 sesiones no es magia. Es el resultado de tomar las decisiones de arquitectura correctas al principio — cuando son baratas — y no cambiarlas después.

Decisión 1: Server Components por defecto

Cada página sigue el mismo patrón: page.tsx es un Server Component que hace el fetch de datos, y pasa los resultados como props a un Client Component que maneja la interactividad.

// 💚 Patrón consistente en 28 páginas
// page.tsx → Server Component (fetch datos)
// page-client.tsx → Client Component (interactividad)

// 📊 Resultado: menos JS en el cliente, más rápido, más seguro
// 🔒 Las queries a BD nunca se exponen al navegador

Esto no es solo rendimiento — es seguridad. Las queries a Supabase con service role nunca llegan al bundle del cliente. Y el patrón se repite idéntico en todas las páginas, lo que hace el desarrollo predecible.

Decisión 2: RLS como red de seguridad

En un SaaS multi-tenant, un bug que filtre datos entre empresas es el fin. Nuestra solución: Row Level Security de PostgreSQL en todas las tablas.

// 💚 Incluso si el código tiene un bug y olvida WHERE tenant_id = X
// La base de datos RECHAZA el acceso
// Es una red de seguridad a nivel de infraestructura

// 📊 14 tablas × 23+ políticas RLS
// 🔒 Funciones SECURITY DEFINER con search_path = public

Con RLS, no dependes de que cada query tenga el filtro correcto. La base de datos lo aplica siempre. Es la diferencia entre "confío en que el código es correcto" y "es imposible acceder a datos de otro tenant".

Decisión 3: Billing como contexto transversal

Cuando la suscripción de un tenant expira, el usuario puede ver sus datos pero no modificarlos. Esto tiene que funcionar en todas las páginas, todos los endpoints, sin excepción.

Lo resolvimos con un React Context (BillingProvider) que expone isReadOnly a toda la app. En el servidor, una función checkBillingWriteAccess() devuelve 403 o nada. Los superadmins bypasean todo.

Un cron diario purga datos 90 días después de la expiración, respetando el orden de foreign keys. Simple, predecible, imposible de olvidar.

Decisión 4: Stripe desde el día 1

No "añadimos pagos después". Stripe estuvo desde la primera sesión con el modelo de pricing definido:

1
Trial 14 días — sin tarjeta, funcionalidad completa. Banner con countdown.
2
Per-seat billing — se cuentan usuarios activos del tenant al crear checkout. Si añades empleados, se recalcula.
3
4 webhooks — checkout completado, subscription actualizada, subscription eliminada, pago fallido. Cada uno actualiza estado en BD.
4
Test Clocks — simulamos ciclos de vida de suscripción completos en desarrollo antes de ir a producción.

El dato que más nos enorgullece

0

Deploy failures en producción.
231 commits, 17 sesiones, cada push directo a main.

Esto no es suerte. Es el resultado de Server Components que se verifican en build, TypeScript strict sin any, 52 tests que cubren la lógica más crítica, y un stack que elimina categorías enteras de errores (RLS en vez de filtros manuales, Stripe webhooks en vez de polling).

La evolución sesión a sesión

Para que se vea que no es un MVP disfrazado — esta es la progresión real del producto:

s1-3
Fundación — Auth, multi-tenant RLS, CRUD gastos, OCR con Claude Vision, categorías
s4-6
Workflow — Informes de gastos, flujo de aprobación, notificaciones email y push
s7-9
Fiscalidad — Multi-IVA, deducibilidad automática, IVA recuperable, consejos fiscales IA
s10-13
Monetización — Stripe, per-seat billing, planes, trial, portal, webhooks, read-only mode
s14-17
Producto — Kilometraje, dietas, campos custom, políticas de gasto, estadísticas, RGPD, testing, refactoring

Cosas que haríamos igual

  1. Supabase en vez de un backend custom. Auth, RLS, Storage, Realtime — todo incluido. Nos ahorró semanas de infraestructura.
  2. Vercel Pro con deploy desde main. Cada push es un deploy. Si falla el build, no llega a producción. Simple.
  3. shadcn/ui como base de componentes. Componentes de calidad que se copian en tu proyecto (no dependencia npm). Se customiza todo.
  4. Stripe desde el día 1. El billing no es algo que "añades después". Es la columna vertebral del negocio.
  5. 52 tests en la lógica fiscal. La fiscalidad española tiene demasiados edge cases para confiar en "funciona en mi máquina".
⚠️

No es rápido por ser simple — es rápido por ser disciplinado

17 sesiones con 24.400 líneas de código no es "programar rápido y que salga lo que salga". Es elegir un stack que elimina categorías de problemas (RLS, Server Components, TypeScript strict), un patrón que se repite en todas las páginas (server fetch + client render), y no perder tiempo en decisiones que ya están tomadas.

Los datos finales

Líneas de código
24.400
TypeScript strict, zero any
Dependencias prod
21
0 vulnerabilidades npm
Tablas PostgreSQL
14
todas con RLS + tenant isolation
Integraciones externas
6
Claude AI, Stripe, Supabase, Resend, Push, Turnstile

🛠️ Stack completo del proyecto

Next.js 16 React 19 TypeScript (strict) Supabase (PostgreSQL + RLS) Tailwind CSS v4 shadcn/ui Claude Vision API Stripe (per-seat) Vercel Pro Recharts ExcelJS + jsPDF Vitest Resend Web Push API

¿Tienes una idea de producto SaaS y necesitas velocidad sin sacrificar calidad? En Nexus Code construimos productos SaaS B2B completos con Next.js, Supabase, Stripe y despliegue continuo. Del concepto al primer cliente en semanas. Cuéntanos tu proyecto.

¿Te ha resultado útil?

4.5 / 5 — 12 valoraciones

¿Tienes una idea de SaaS y necesitas velocidad?

Construimos productos SaaS completos con Next.js, Supabase y Vercel. Del concepto al primer cliente en semanas, no en meses.

Cuéntanos tu proyecto