Full-stack TypeScript en 2025: lo bueno, lo regular, lo malo
Índice de contenidos
- Puntos clave
- Lo bueno
- Lo regular
- Lo malo
- Cuándo tiene sentido
- Lo que viene
- Preguntas frecuentes
- ¿Necesito generar código con OpenAPI o Protobuf para compartir tipos entre frontend y backend en TypeScript?
- ¿Es Node.js suficiente para un backend con carga de CPU intensa?
- ¿Qué herramienta de testing elijo para un proyecto TypeScript nuevo?
- Fuentes
Full-stack TypeScript con Next.js y tRPC elimina la duplicación de tipos entre frontend y backend, pero no es la solución universal: Node se queda corto en cargas CPU-bound, el testing sigue fragmentado y los proyectos grandes acaban dividiéndose. Es la mejor opción para productos pequeños y medianos con equipos ágiles, no para sistemas de alto rendimiento sostenido.
Durante varios años me resistí a la idea de full-stack TypeScript. Venía de arquitecturas con Go o Python en el backend y TypeScript solo en el frontend, y la idea de unificar me parecía una concesión por conveniencia. En los últimos dos años he trabajado en varios proyectos donde el stack es íntegramente TypeScript, y tengo opiniones más matizadas que las de entonces.
El objetivo de este post no es convencer a nadie de que adopte full-stack TypeScript, ni desaconsejarlo. Es ofrecer un repaso honesto de qué he visto funcionar, qué sigue siendo problema y en qué tipo de proyecto la elección tiene sentido.
Puntos clave
-
El modelo de datos compartido y herramientas como tRPC eliminan la capa de generación de código entre frontend y backend.
-
Los frameworks modernos (Next.js App Router, Remix, SvelteKit) han difuminado la línea cliente-servidor de manera que simplifica proyectos de tamaño mediano.
-
El ecosistema de testing sigue fragmentado; Vitest es el mejor punto de partida para proyectos nuevos.
-
Para servicios CPU-bound o con requisitos de latencia estricta, Go o Rust siguen siendo más apropiados que Node.
-
La elección tiene sentido en productos pequeños a medianos con equipos pequeños e iteración rápida.
Lo bueno
La ventaja más inmediata es el modelo de datos compartido. Cuando el mismo tipo User o Product vive en frontend y backend, y no tienes que escribirlo dos veces ni sincronizarlo con un generador externo, el coste cognitivo baja y los errores por desalineación desaparecen. Es de esas cosas que no aprecias hasta haberlas vivido, porque la alternativa (OpenAPI, Protobuf, duplicación manual) funciona, pero añade un paso constante de mantenimiento.
Herramientas como tRPC han convertido esto en algo mucho más que el tipo compartido. Con tRPC, un procedimiento en el backend es automáticamente una función tipada en el frontend, con autocompletado completo y validación en serialización y deserialización. No hace falta capa de generación de código externa, como explica la documentación oficial de tRPC[1]).
La segunda ventaja real es el ecosistema de frameworks que asume full-stack desde el principio. Next.js con App Router, Remix (ahora fusionado con React Router), SvelteKit, Nuxt: todos están diseñados para que escribir una función en el servidor y consumirla desde el cliente sea trivial. Esta integración se ha vuelto tan buena que la línea entre frontend y backend se ha difuminado, y en proyectos de tamaño pequeño a mediano eso simplifica mucho.
Tercero, la calidad del tipado ha mejorado mucho con TypeScript 5.x: inferencia más potente, tipos condicionales más expresivos, constantes. Ya repasé cambios concretos como la inferencia de predicados de tipo en TypeScript 5.5, anunciada en el blog oficial de TypeScript[2]. El tipo derivado de un esquema Zod, por ejemplo, suele ser más expresivo que lo que escribirías a mano. La validación en tiempo de ejecución y la verificación en tiempo de compilación usan la misma fuente de verdad.
Logo de Next.js, el framework full-stack más extendido en el ecosistema TypeScript (Imagen: Vercel, CC BY-SA 4.0, vía Wikimedia Commons)
Lo regular
Hay zonas donde el balance es más ambiguo:
-
Rendimiento del backend en Node.js: razonable para servicios IO-bound, pero no brillante para servicios CPU-bound o con latencia predecible estricta. La aparición de Bun y Deno ha puesto presión saludable, y Bun especialmente tiene características interesantes: rendimiento de startup, ecosistema más ligero. En producción, Node sigue siendo el destino por defecto, por compatibilidad npm y soporte empresarial. Vale la pena revisar Deno 2.0 para proyectos nuevos.
-
Gestión de dependencias: npm y pnpm han estabilizado mucho, pero tener 1.200 paquetes en node_modules en un proyecto modesto sigue siendo lo normal. Cada ataque a la cadena de suministro en paquetes populares recuerda que esta es una superficie grande;
npm audit(ver la documentación oficial[3]) ayuda, pero no la elimina. -
Tamaño del bundle frontend: con React Server Components y mejor tree-shaking ha mejorado, pero un proyecto se hincha sin darse cuenta hasta que el tiempo de carga lo hace evidente.
Lo malo
Hay cosas que siguen siendo francamente problemáticas:
-
El ecosistema de testing está fragmentado. Vitest[4] se ha estabilizado como alternativa seria a Jest y es claramente mejor para proyectos nuevos, pero migrar proyectos grandes sigue siendo un dolor. Ninguna librería de mocking se ha convertido en estándar claro.
-
La fricción entre frontend y backend en servicios realmente grandes vuelve a aparecer. Cuando el proyecto crece lo suficiente, lo que parecía una sola base de código se parte en dos o tres, y el beneficio de compartir tipos se reduce.
-
La experiencia con TypeScript en librerías de terceros sigue siendo desigual. Algunas librerías están excelentemente tipadas; otras publican tipos incompletos o incorrectos. Cuando te topas con una definición mala, arreglarlo implica forkear, escribir declaraciones locales o aguantar con
any. -
La compilación sigue pesando. Un proyecto TypeScript grande compila más lento que el equivalente en Go o Rust. Las mejoras en TypeScript 5.x (project references[5], incremental) han aliviado pero no eliminado el problema.
Cuándo tiene sentido
Después de probar varias configuraciones, mi recomendación es:
-
Buena elección: proyectos de producto de tamaño pequeño a mediano donde el equipo es pequeño, la iteración es corta y la cercanía entre frontend y backend aporta. SaaS B2B, dashboards internos, herramientas admin, productos en etapa temprana.
-
Elección menos clara: sistemas con componentes heterogéneos, donde el backend hace trabajo pesado (procesamiento de datos, cálculos complejos, streaming de tiempo real), o donde más de un equipo independiente trabaja en el mismo sistema.
-
Mala elección: cuando el requisito real es rendimiento sostenido alto, bajo uso de memoria o despliegue en entornos restringidos. Ahí hay lenguajes diseñados para eso.
Lo que viene
Espero ver consolidación más que revolución. Bun va a seguir presionando a Node, lo cual es bueno.
Los frameworks como Next.js van a seguir difuminando la línea entre cliente y servidor. La tipificación automática de esquemas de base de datos (à la Drizzle ORM) va a extenderse. Y el ecosistema de herramientas como tRPC va a tener alternativas maduras en otras plataformas.
Full-stack TypeScript ha ganado un hueco legítimo y probablemente va a mantenerlo durante años. No es la solución universal, pero para una franja real del mercado es una elección razonable que, bien implementada, da resultados buenos.
Lee también la versión en inglés: Full-stack TypeScript in 2025: the good, the meh, the bad.
Fuentes:
- tRPC: Documentación oficial: Quickstart[1]
- Anuncio oficial de TypeScript 5.5 (TypeScript Blog, Microsoft)[2]
- Vitest: Guía oficial[4]
- TypeScript: Project References (Handbook oficial)[5]
- npm Docs: About audit reports[3]
Preguntas frecuentes
¿Necesito generar código con OpenAPI o Protobuf para compartir tipos entre frontend y backend en TypeScript?
No. Cuando el mismo tipo User o Product vive en frontend y backend no hace falta escribirlo dos veces ni sincronizarlo con un generador externo. Herramientas como tRPC van más allá: un procedimiento en el backend es automáticamente una función tipada en el frontend, con autocompletado completo y validación en serialización y deserialización, sin capa de generación de código. Un esquema Zod aporta además la misma fuente de verdad para la validación en tiempo de ejecución y la verificación en compilación.
¿Es Node.js suficiente para un backend con carga de CPU intensa?
Para servicios IO-bound su rendimiento es razonable, pero no brilla en servicios CPU-bound ni con requisitos de latencia predecible estricta; ahí Go o Rust siguen siendo más apropiados. Bun y Deno han puesto presión saludable, y Bun destaca en rendimiento de arranque y ecosistema más ligero. En producción, Node sigue siendo el destino por defecto, por compatibilidad con npm y soporte empresarial. Si el requisito real es rendimiento sostenido alto o bajo uso de memoria, full-stack TypeScript es mala elección.
¿Qué herramienta de testing elijo para un proyecto TypeScript nuevo?
Vitest: se ha estabilizado como alternativa seria a Jest y es claramente mejor para proyectos nuevos. El problema está en migrar proyectos grandes, que sigue siendo un dolor, y en las librerías de mocking, que no tienen un estándar claro. El ecosistema de testing es una de las zonas francamente problemáticas del stack, junto con los tipos incompletos o incorrectos de algunas librerías de terceros. También pesa la compilación lenta de proyectos grandes, aliviada pero no resuelta por project references e incremental en TypeScript 5.x.