
Logrando Type Safety entre Angular y NestJS con el Nuevo Generador de Prisma
Tabla de Contenidos
Tabla de Contenidos
Uno de los desafíos más emocionantes en el desarrollo full-stack es mantener la seguridad de tipos en toda tu aplicación. Cuando trabajas con bases de datos, APIs backend e interfaces frontend, mantener tus tipos sincronizados puede convertirse en una pesadilla. ¿Pero qué pasaría si te dijera que hay una forma de generar tipos una vez y usarlos en todas partes?
Prisma ha introducido un nuevo generador desde la versión 6.x que cambia el juego completamente. Este nuevo generador nos da algo más tangible sobre de dónde vienen nuestros tipos y el cliente de Prisma, abordando el hecho de que modificar node_modules (excepto a través de npm/yarn) no es exactamente una mejor práctica.
En esta guía, te mostraré cómo aprovechar este nuevo generador de Prisma para compartir tipos entre tu backend NestJS y tu frontend Angular usando Nx como nuestra herramienta de monorepo.
Pero, ¿qué hace que este nuevo generador sea tan genial?
El nuevo generador de Prisma crea tipos y enums fuera de node_modules, lo que significa que podemos reutilizar estos tipos en toda nuestra stack. Esto es enorme porque significa:
Type Safety en Todas Partes: Tu esquema de base de datos se convierte en la única fuente de verdad para toda tu aplicación
No Más Duplicación: Deja de escribir las mismas interfaces en tu backend y frontend
Mejor Experiencia de Desarrollador: IntelliSense y autocompletado funcionan sin problemas en toda tu stack
Menos Bugs: Detecta desajustes de tipos en tiempo de compilación, no en tiempo de ejecución
En términos simples, este nuevo generador te ayuda a:
Generar Tipos Reutilizables: Crear interfaces TypeScript y enums que se pueden compartir entre proyectos
Mantener Consistencia: Asegurar que tu frontend y backend estén siempre sincronizados con tu esquema de base de datos
Mejorar Productividad: Pasar menos tiempo escribiendo tipos boilerplate y más tiempo construyendo características
Escalar Mejor: A medida que tu aplicación crece, tus tipos crecen con ella automáticamente
Configurando Nuestro Proyecto
Usaremos Nx para crear un monorepo que contiene:
Una biblioteca compartida con nuestros tipos generados por Prisma
Una API backend NestJS
Una aplicación frontend Angular
Al final de este post, tendrás un ejemplo completo funcionando disponible en GitHub que demuestra este poderoso patrón en acción.
Comencemos creando nuestro workspace de Nx:
npx create-nx-workspace@latest
Cuando se te solicite, usa estas opciones de configuración:
✔ Where would you like to create your workspace? · prisma-nest-angular-workspace
✔ Which stack do you want to use? · angular
✔ Integrated monorepo, or standalone project? · integrated
✔ Application name · blog-app
✔ Which bundler would you like to use? · esbuild
✔ Default stylesheet format · scss
✔ Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? · No
✔ Which unit test runner would you like to use? · vitest
✔ Test runner to use for end to end (E2E) tests · none
✔ Which CI provider would you like to use? · skip
✔ Would you like remote caching to make your build faster? · skip
¡Genial! Ahora tenemos nuestra aplicación Angular configurada. A continuación, agreguemos NestJS a nuestro monorepo.
Agregando NestJS a Nuestro Monorepo
Primero, necesitamos instalar el plugin de Nx NestJS:
npx nx add @nx/nest
Ahora podemos generar nuestra aplicación NestJS. Puedes hacer esto ya sea a través de la extensión Nx de VS Code o mediante la línea de comandos:
npx nx generate @nx/nest:application --directory=apps/blog-api --linter=eslint --name=blog-api --strict=true --unitTestRunner=jest --no-interactive
¡Perfecto! Ahora tenemos ambas aplicaciones en nuestro monorepo:
Frontend Angular en
apps/blog-appBackend NestJS en
apps/blog-api
Creando una Biblioteca Compartida para Tipos de Prisma
¡Ahora viene la parte emocionante! Necesitamos crear una biblioteca compartida que albergará nuestros tipos generados por Prisma. Esta biblioteca será el lugar central donde tanto nuestras aplicaciones Angular como NestJS pueden importar tipos.
Creemos nuestra biblioteca compartida:
npx nx generate @nx/js:library --directory=libs/prisma-generated --bundler=none --linter=eslint --name=prisma-generated --unitTestRunner=none
Esto crea una nueva biblioteca en libs/prisma-generated que tanto nuestro frontend como backend pueden usar.
Configurando Múltiples Puntos de Entrada
Aquí es donde se pone interesante. Queremos crear dos puntos de entrada separados en nuestra biblioteca:
Punto de entrada Node - Para uso del lado del servidor (NestJS)
Punto de entrada de Tipos - Para uso del lado del cliente (Angular)
Esta separación nos permite:
Importar el cliente completo de Prisma en nuestro backend
Importar solo los tipos ligeros en nuestro frontend
Mantener límites limpios entre código de cliente y servidor
¡Veamos cómo configurar estos puntos de entrada!
Configurando Rutas de TypeScript
Primero, necesitamos actualizar nuestro tsconfig.base.json para crear rutas de importación convenientes para nuestros dos puntos de entrada. Agrega estas rutas a la sección paths:
...
"paths": {
"@shared/prisma-generated/client": [
"libs/prisma-generated/src/lib/client.ts"
],
"@shared/prisma-generated/types": [
"libs/prisma-generated/src/lib/types.ts"
]
}
...
Esta configuración nos permite:
Importar el cliente de Prisma usando
@shared/prisma-generated/client(para NestJS)Importar solo tipos usando
@shared/prisma-generated/types(para Angular)
Creando Nuestros Archivos de Punto de Entrada
Ahora creemos los dos archivos que servirán como nuestros puntos de entrada. En tu directorio libs/prisma-generated/src/, crea:
client.ts - Para uso del lado del servidor:
// This file will export the Prisma client and related functionality
// We'll populate this after setting up Prisma
types.ts - Para uso del lado del cliente:
// This file will export only types and enums
// We'll populate this with generated Prisma types
Estos archivos de marcador de posición se llenarán una vez que configuremos el nuevo generador de Prisma. ¡La belleza de esta configuración es que tu frontend nunca importará accidentalmente código del lado del servidor!
Configurando Prisma
Ahora inicialicemos Prisma en nuestro proyecto. Usaremos PostgreSQL como nuestro proveedor de base de datos y configuraremos la salida para generar archivos directamente en nuestra biblioteca compartida.
Primero, navega a la raíz de tu workspace e inicializa Prisma:
npx prisma init --datasource-provider postgresql --output ../libs/prisma-generated/src/lib/generated
Este comando hace varias cosas importantes:
Crea un archivo
prisma/schema.prismacon configuración de PostgreSQLConfigura un archivo
.envcon variables de conexión de base de datos (recuerda agregarlo a nuestro.gitignore)Configura el directorio de salida para generar archivos en nuestra biblioteca compartida
La bandera --output es crucial aquí porque le dice a Prisma que genere los archivos del cliente directamente en nuestro directorio libs/prisma-generated/src/lib/generated en lugar de la ubicación predeterminada de node_modules. ¡Esto es exactamente lo que queremos para compartir tipos en nuestro monorepo!
Configurando el Nuevo Generador
¡Aquí es donde sucede la magia! Prisma crea el archivo de esquema en prisma/schema.prisma. Necesitamos hacer un cambio importante para usar el nuevo generador.
Abre prisma/schema.prisma y actualiza la configuración del generador. Cambia esto:
generator client {
provider = "prisma-client-js"
output = "../libs/prisma-generated/src/lib/generated"
}
A esto:
generator client {
provider = "prisma-client" // cambiar de prisma-client-js a prisma-client
output = "../libs/prisma-generated/src/lib/generated"
}
Importante: El nuevo generador usa "prisma-client" en lugar de "prisma-client-js". Este nuevo generador es lo que nos da los tipos tangibles y compartibles que podemos usar en todo nuestro monorepo. Aunque prisma-client-js sigue siendo el predeterminado, prisma-client es el futuro y proporciona una experiencia de desarrollador mucho mejor para configuraciones de monorepo.
Importante: Agregando Archivos Generados a .gitignore
Antes de generar nuestros archivos, necesitamos agregar la carpeta generada a nuestro .gitignore. Esto es crucial porque:
Al desarrollar localmente o en CI,
npx prisma generatedetecta tu sistema operativoDescarga el ejecutable apropiado para tu sistema operativo (Windows, macOS, Linux)
Regenera todos los tipos y archivos del cliente específicos para tu entorno
Agrega esta línea a tu archivo .gitignore:
# Prisma generated files
libs/prisma-generated/src/lib/generated/
Esto asegura que:
Los archivos generados no se comprometan al control de versiones
Cada desarrollador obtiene los binarios correctos para su sistema operativo
Los pipelines de CI/CD generan archivos nuevos para su entorno
No hay conflictos entre diferentes sistemas operativos
Generando Nuestros Archivos de Prisma
Ahora generemos nuestro cliente y tipos de Prisma para ver la magia en acción:
npx prisma generate
¡Este comando creará todos los archivos necesarios en libs/prisma-generated/generated/ incluyendo tipos, enums y el cliente de Prisma que ahora podemos compartir en todo nuestro monorepo!
Explorando los Archivos Generados
Después de ejecutar npx prisma generate, verás una nueva estructura de archivos en tu carpeta libs/prisma-generated/src/lib/generated/:
libs/prisma-generated/
└── src/
└── lib/
└── generated/
├── internal/
├── models/
├── browser.ts
├── client.ts
├── commonInputTypes.ts
├── enums.ts
├── libquery_engine-darwin-arm64.dylib.node // binario específico del sistema operativo
└── models.ts
Estos archivos generados contienen:
client.ts: El cliente principal de Prisma para uso del lado del servidor
models.ts: Todos los tipos de modelos de tu base de datos
enums.ts: Enums de base de datos como enums de TypeScript
browser.ts: Cliente compatible con navegador (versión más ligera)
commonInputTypes.ts: Tipos de entrada para consultas y mutaciones
libquery_engine-*.node: Binario del motor de consultas específico del sistema operativo
¡Ahora podemos conectar nuestros puntos de entrada para exponer estos tipos generados!
El Problema Crítico con las Compilaciones Frontend
Aquí es donde encontramos el detalle más importante de toda esta configuración. Prisma genera:
client.ts: El cliente completo para uso del lado del servidor (NestJS)
browser.ts: Un cliente "compatible con navegador" para uso frontend
¡Sin embargo, hay una trampa! Aunque browser.ts está destinado para uso en navegador, todavía contiene dependencias de Node.js que romperán tu proceso de compilación de Angular.
La Solución: Exportaciones Solo de Tipo
Aquí es donde sucede la magia. Usaremos la exportación type de TypeScript para filtrar todo lo que no sea un tipo, dejándonos con tipos limpios y libres de Node.js para nuestro frontend.
Actualiza tu archivo libs/prisma-generated/src/lib/types.ts:
// Export clean types for frontend (no Node.js dependencies)
export type * from './generated/browser';
export type * from './generated/enums';
La palabra clave type es crucial aquí porque:
Exporta solo tipos e interfaces de TypeScript
Filtra cualquier código JavaScript real o dependencias de Node.js
Asegura que tu compilación de Angular no falle debido a importaciones del lado del servidor
Tu frontend obtiene toda la seguridad de tipos sin ningún equipaje de tiempo de ejecución
¡Este enfoque te da lo mejor de ambos mundos: seguridad de tipos completa en tu frontend sin romper tu proceso de compilación!
Configurando el Punto de Entrada del Cliente
Ahora configuremos nuestro punto de entrada del lado del servidor. Actualiza tu archivo libs/prisma-generated/src/lib/client.ts:
// Export Prisma client for backend use
export { PrismaClient, Prisma } from './generated/client';
Este punto de entrada:
Exporta el
PrismaClientcompleto con toda su funcionalidadExporta el namespace
Prismacon utilidades y helpersEstá destinado solo para uso del lado del servidor (NestJS)
Contiene dependencias de Node.js que romperían las compilaciones frontend
Probando Nuestra Configuración con un Ejemplo Real
Creemos un ejemplo práctico para ver nuestros tipos compartidos en acción. Agrega este modelo y enum a tu archivo prisma/schema.prisma:
// Enum for blog post status
enum PostStatus {
DRAFT
PUBLISHED
ARCHIVED
}
// Simple table for blog posts
model Post {
id String @id @default(cuid())
title String
content String?
status PostStatus @default(DRAFT)
authorName String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("posts")
}
Ahora regenera tu cliente de Prisma para crear los tipos:
npx prisma generate
Esto generará:
Tipo Post - Interfaz TypeScript para nuestra publicación de blog
Enum PostStatus - Enum TypeScript con valores DRAFT, PUBLISHED, ARCHIVED
Tipos de entrada - Para crear y actualizar publicaciones
Helpers de consulta - Para el cliente de Prisma del backend
¡Ahora podemos usar estos tipos en toda nuestra stack!
Usando Tipos en Angular (Frontend)
¡Ahora veamos la magia en acción! En tu aplicación Angular, puedes importar y usar los tipos generados. Actualiza tu apps/blog-app/src/app/app.component.ts:
import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { NxWelcome } from './nx-welcome';
import { Post } from '@shared/prisma-generated/types';
@Component({
imports: [NxWelcome, RouterModule],
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.scss',
})
export class App {
protected title = 'blog-app';
protected posts: Post[] = [];
}
Observa cómo:
Importamos solo el tipo
Postusando@shared/prisma-generated/typesObtenemos intellisense completo de TypeScript para la interfaz Post
No se incluyen dependencias de Node.js en nuestra compilación de Angular
El tipo incluye todas las propiedades:
id,title,content,status,authorName,createdAt,updatedAt
¡Tu compilación de Angular funcionará perfectamente porque solo estamos importando tipos, no código del lado del servidor!
Usando Tipos en NestJS (Backend)
¡La belleza de esta configuración es que también podemos usar los mismos tipos en nuestro backend! En tu aplicación NestJS, puedes importar tanto el cliente como los tipos. Por ejemplo, en tu apps/blog-api/src/app/app.service.ts:
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@shared/prisma-generated/client'; // client
import { Post } from '@shared/prisma-generated/types'; // all the types
@Injectable()
export class AppService {
private prisma = new PrismaClient();
async getAllPosts(): Promise<Post[]> {
return this.prisma.post.findMany();
}
}
Observa cómo:
Importamos
PrismaClientde@shared/prisma-generated/clientpara operaciones de base de datosImportamos el tipo
Postde@shared/prisma-generated/typespara seguridad de tiposTanto Angular como NestJS usan exactamente el mismo tipo
PostIntellisense perfecto de TypeScript tanto en frontend como en backend
Aplicando Límites con Nx
Aquí hay algo súper genial: podemos usar las reglas enforce-boundary de Nx para prevenir importaciones inadecuadas y asegurar que nuestra arquitectura se mantenga limpia. Esto previene:
Que Angular importe accidentalmente el cliente completo de Prisma
Que el backend importe accidentalmente código específico del navegador
Mantener la separación adecuada entre preocupaciones de cliente y servidor
Puedes configurar estas reglas en tu .eslint.config.mjs para aplicar que Angular solo pueda importar de @shared/prisma-generated/types y NestJS solo pueda importar de @shared/prisma-generated/client.
¡Esta red de seguridad arquitectónica asegura que tu equipo no pueda accidentalmente romper la compilación o introducir dependencias no deseadas!
Lo Que Logramos
En esta guía, hemos construido una poderosa configuración full-stack que resuelve uno de los mayores desafíos en el desarrollo web moderno: mantener la seguridad de tipos en toda tu aplicación. Esto es lo que logramos:
🏗️ Configuración de Monorepo
Creamos un workspace de Nx con aplicaciones Angular y NestJS
Configuramos una biblioteca compartida para tipos generados por Prisma
🔧 Configuración de Prisma
Usamos el nuevo generador
prisma-client(en lugar deprisma-client-js)Configuramos la salida para generar archivos en nuestra biblioteca compartida
Agregamos archivos generados a
.gitignorepara soporte adecuado de CI/CD
📦 Puntos de Entrada Inteligentes
Creamos puntos de entrada separados para cliente (
@shared/prisma-generated/client) y tipos (@shared/prisma-generated/types)Usamos
export type *para filtrar dependencias de Node.js para compilaciones frontendAseguramos que las compilaciones de Angular funcionen sin código del lado del servidor
🎯 Type Safety en Todas Partes
Compartimos los mismos tipos
PostyPostStatusentre frontend y backendMantuvimos intellisense completo de TypeScript en ambas aplicaciones
Eliminamos la duplicación de tipos y problemas de sincronización
🛡️ Seguridad Arquitectónica
Aprovechamos las reglas enforce-boundary de Nx para prevenir importaciones inadecuadas
Protegimos contra la mezcla accidental de código de cliente y servidor
Pruébalo Tú Mismo
¿Listo para ver esto en acción? He creado un ejemplo completo funcionando que demuestra todo lo que cubrimos en este post. Puedes clonar el repositorio y explorar la configuración:
🔗 Repositorio GitHub: https://github.com/oidacra/shared-prisma-types-monorepo
El repositorio incluye:
Configuración completa de monorepo Nx
Aplicaciones Angular y NestJS funcionando
Biblioteca de tipos Prisma compartidos
Implementaciones de ejemplo de modelo y servicio de publicación de blog
Entorno de desarrollo listo para ejecutar
¡Clónalo, ejecuta npm install, configura tu conexión de base de datos y ve el poder de los tipos compartidos de Prisma en acción!
Verificando que Todo Funciona
Para confirmar que nuestra configuración está funcionando correctamente y que ambas aplicaciones se compilan sin errores, puedes ejecutar:
npx nx run blog-api:build
npx nx run blog-app:build
¡Ambas compilaciones deberían completarse exitosamente sin errores! Esto prueba que:
Angular se compila limpiamente sin dependencias de Node.js
NestJS tiene acceso al cliente completo de Prisma
Nuestra estrategia de separación de tipos funciona perfectamente
Los tipos compartidos están configurados correctamente
Este enfoque transformará cómo construyes aplicaciones full-stack, dándote la confianza de que tu frontend y backend están siempre en perfecta sincronización. ¡Feliz codificación! 🚀


