Talently
Talently
TypeORM

TypeORM

El ORM TypeScript para Node.js con soporte para múltiples bases de datos

TypeORM es un ORM para Node.js y TypeScript que soporta bases de datos relacionales y no relacionales, incluyendo PostgreSQL, MySQL, SQLite, MongoDB y más. Usando decoradores de TypeScript para definir entidades y relaciones, provee dos patrones de uso, Active Record y Data Mapper, con soporte completo para migraciones, relaciones complejas y es la librería de persistencia oficial recomendada en el ecosistema NestJS.

TypeScriptNode.jsDecoratorsSQL

Demanda del mercado

TypeORM tiene alta demanda especialmente en proyectos NestJS donde es la librería de persistencia más usada. Su adopción ha crecido significativamente con el crecimiento de NestJS y TypeScript en el desarrollo backend.

ORM oficial recomendado en NestJSAlta demanda en proyectos TypeScriptCrecimiento acelerado con ecosistema NestJS

Requisitos técnicos

Intermediate

Requiere dominio sólido de TypeScript incluyendo decoradores, generics y tipos avanzados. Familiaridad con bases de datos relacionales, SQL y conceptos de ORM como entidades, relaciones y migraciones es esencial para proyectos TypeORM en producción.

Casos de uso

Proyectos Reales

TypeORM se utiliza para desarrollar:

  • Capa de persistencia en aplicaciones NestJS
  • APIs TypeScript con modelos de datos relacionales
  • Sistemas con múltiples bases de datos en la misma aplicación
  • Proyectos que migran de Sequelize a TypeScript con tipado estricto

Tipos de Empresa

TypeORM es adoptado por:

  • Startups con stack NestJS y TypeScript
  • Empresas migrando backends Node.js a TypeScript
  • Equipos que desarrollan APIs con NestJS y PostgreSQL
  • Compañías con proyectos TypeScript full-stack

Escenarios de Producción

TypeORM es ampliamente utilizado en entornos productivos como:

  • APIs REST y GraphQL con NestJS y TypeORM
  • Sistemas con modelos de dominio complejos y tipados
  • Aplicaciones con múltiples fuentes de datos relacionales
  • Backends con repositorios tipados para cada entidad

Escalabilidad

TypeORM ofrece múltiples mecanismos para escalar aplicaciones:

  • Connection pooling para gestión eficiente de conexiones
  • QueryBuilder para queries complejas tipadas
  • Caché de queries integrado
  • Migraciones automáticas y manuales para gestión del esquema

Ventajas y Desventajas

Ventajas

TypeScript de primera clase con decoradores para definir entidades y relaciones.

Soporte para los patrones Active Record y Data Mapper en el mismo proyecto.

Integración oficial y profunda con NestJS mediante @nestjs/typeorm.

Desventajas

Historial de mantenimiento inconsistente con períodos de actividad reducida.

Bugs conocidos en algunas funcionalidades avanzadas que han tardado en resolverse.

Prisma ha ganado tracción como alternativa más moderna con mejor experiencia de desarrollo.

Comparación

Ventajas de Prisma

  • Schema declarativo como única fuente de verdad
  • Tipos generados automáticamente sin decoradores
  • Mejor experiencia de desarrollo y menos errores en runtime

Consideraciones

Prisma tiene mejor experiencia de desarrollo TypeScript con tipos inferidos automáticamente. TypeORM es preferible cuando se necesita el patrón Active Record, cuando se hereda código TypeORM existente o cuando la integración con NestJS mediante módulos específicos es prioritaria.

Preguntas básicas

TypeORM usa decoradores TypeScript para definir entidades y relaciones directamente en las clases, generando un código más expresivo y tipado. La integración con NestJS mediante @nestjs/typeorm es oficial y profunda, con inyección de repositorios tipados directamente en los servicios.
Active Record extiende BaseEntity en las entidades y tiene los métodos de persistencia como save, find y remove directamente en la clase de la entidad. Data Mapper separa la lógica de persistencia en repositorios independientes de la entidad. Data Mapper es preferible para proyectos grandes donde se quiere separar el dominio de la infraestructura de persistencia.
Usando el decorador @Entity en la clase, @Column para cada propiedad que mapea a una columna, @PrimaryGeneratedColumn para la clave primaria autogenerada, y @OneToMany, @ManyToOne, @ManyToMany o @OneToOne para las relaciones con sus decoradores correspondientes en ambos lados de la relación.
Las entidades son clases TypeScript con tipos definidos que el IDE puede verificar, las relaciones están tipadas y el compilador detecta errores de acceso a propiedades inexistentes. Los repositorios son genéricos Repository que proveen métodos tipados, eliminando una clase de errores en runtime que ORMs JavaScript solo detectan en ejecución.
TypeORM puede generar migraciones automáticamente comparando las entidades con el esquema actual de la base de datos, o se crean manualmente para mayor control. En producción synchronize debe estar desactivado y las migraciones se aplican explícitamente, garantizando que los cambios de esquema son revisados y versionados antes de aplicarse.
Para queries complejas con múltiples JOINs condicionales, subqueries, agregaciones o condiciones dinámicas que los métodos find del repositorio no pueden expresar de forma limpia. QueryBuilder permite construir SQL complejo de forma programática manteniendo el tipado de TypeScript.
Ocurre cuando se carga una lista de entidades y luego se accede a relaciones lazy de cada una, generando una query por entidad. Se resuelve usando eager loading con relations en las opciones de find, o con leftJoinAndSelect en QueryBuilder para cargar las relaciones en la misma query con JOIN.
En proyectos NestJS con TypeScript donde la integración oficial y los repositorios tipados son prioritarios, cuando el equipo prefiere el patrón de decoradores similar a Java o cuando se necesita un ORM con soporte tanto para bases de datos relacionales como MongoDB en la misma librería.

Preguntas técnicas

Creando una entidad intermedia explícita que representa la tabla de unión con sus propias columnas adicionales, y usando relaciones ManyToOne desde esa entidad hacia las dos entidades originales. Esto es necesario cuando la relación muchos a muchos tiene atributos propios que no caben en una tabla de unión simple.
Implementando la interfaz EntitySubscriberInterface y decorando la clase con @EventSubscriber. Se implementan los métodos como beforeInsert, afterUpdate o beforeRemove para ejecutar lógica transversal como auditoría, normalización o notificaciones cuando las entidades cambian de estado, sin acoplar esta lógica a los servicios.
Eager loading con eager: true en el decorador de relación carga la relación automáticamente en cada query, lo que puede generar JOINs innecesarios. Lazy loading con lazy: true devuelve una Promise que se resuelve cuando se accede a la relación, requiriendo await. En la práctica se prefiere cargar relaciones explícitamente por query con relations para mayor control.
Añadiendo @DeleteDateColumn en la entidad que TypeORM gestiona automáticamente. Al llamar softDelete o softRemove establece la columna en lugar de eliminar el registro. Las queries normales excluyen registros soft-deleted automáticamente y se incluyen con withDeleted en las opciones de find o en QueryBuilder.
Usando el DataSource o EntityManager con transaction() que recibe una función async con el entityManager de la transacción. En NestJS se puede inyectar DataSource en el servicio y usar dataSource.transaction() para agrupar múltiples operaciones. También se puede usar el decorador @Transaction con @TransactionManager aunque este enfoque está deprecado en versiones recientes.
Usando las estrategias de herencia con @TableInheritance en la entidad padre. STI con discriminatorColumn almacena todas las subclases en una tabla. CTI con @ChildEntity en cada subclase crea tablas por subclase con JOINs. Se elige según los requisitos de normalización y el patrón de acceso a datos del proyecto.
Configurando el DataSource con las entidades y la ruta de migraciones, usando typeorm migration:generate para generar automáticamente la migración comparando las entidades con el esquema actual, revisando el SQL generado para verificar que es correcto, y ejecutando typeorm migration:run en el proceso de despliegue antes de iniciar la aplicación.
Usando findAndCount que devuelve los registros y el total en una sola llamada para la paginación por offset, con take para el límite y skip para el offset. Para tablas grandes se implementa paginación por cursor usando where con el id del último registro, order consistente y take, evitando el rendimiento degradado de skip alto en tablas con millones de registros.

Preguntas avanzadas

Usando el patrón Data Mapper con repositorios custom que extienden Repository para encapsular las queries específicas del dominio, inyectados en los servicios mediante @InjectRepository. Los módulos de NestJS encapsulan cada dominio con sus entidades, repositorios y servicios, importando TypeOrmModule.forFeature con las entidades del módulo.
Cuando los bugs conocidos de TypeORM impactan funcionalidades críticas del proyecto, cuando el equipo prefiere el modelo declarativo de Prisma con tipos generados sin decoradores, o cuando MikroORM con su Unit of Work más robusto aporta mejor consistencia. La migración es costosa y debe justificarse con problemas reales en el proyecto.
Creando repositorios de escritura con entidades completas para operaciones de dominio y repositorios de lectura con proyecciones SQL optimizadas usando QueryBuilder para las queries de consulta. Los command handlers usan los repositorios de escritura y los query handlers usan los de lectura, permitiendo optimizaciones independientes en cada lado.
Usando transacciones de base de datos para operaciones síncronas donde todos los cambios deben ser atómicos, implementando el patrón Outbox para eventos de dominio que deben dispararse después del commit con garantía de entrega, y usando optimistic locking con @VersionColumn para detectar conflictos en operaciones concurrentes.
Activando el logging de queries para identificar queries lentas o N+1, usando explain analyze en PostgreSQL para analizar los planes de ejecución, añadiendo índices en columnas de filtro frecuente, usando proyecciones con select para traer solo las columnas necesarias, implementando caché de queries de TypeORM para consultas frecuentes y revisando el pool de conexiones.
Usando una base de datos de test SQLite en memoria o PostgreSQL en Docker para tests de integración que verifican el comportamiento real del ORM con la base de datos, mockeando los repositorios con jest.mock para tests unitarios de servicios que no deben depender de la base de datos, y aplicando migraciones antes de los tests con synchronize: true en el entorno de test.

Errores comunes en entrevistas

Dejar synchronize: true en producción permite que TypeORM modifique el esquema automáticamente al arrancar, lo que puede generar pérdida de datos o cambios no revisados. Es uno de los errores de configuración más graves y frecuentes en proyectos TypeORM llevados a producción sin experiencia.
No definir correctamente mappedBy en el lado inverso de las relaciones genera tablas intermedias innecesarias o comportamientos inesperados en las queries. Es un error frecuente en developers nuevos con TypeORM que no entienden cómo se mapean las foreign keys.
Proponer TypeORM para un proyecto nuevo sin evaluar Prisma o MikroORM refleja no seguir el ecosistema ORM TypeScript actual. Se espera conocer que Prisma ha ganado popularidad y poder justificar la elección de TypeORM frente a estas alternativas.
Intentar expresar queries complejas con los métodos find del repositorio cuando QueryBuilder sería más apropiado genera código ilegible o queries ineficientes. No conocer QueryBuilder refleja experiencia limitada con TypeORM en proyectos con requisitos de datos complejos.
No configurar correctamente el pool de conexiones o no cerrar el DataSource en tests genera connection leaks. En producción esto puede agotar el pool de conexiones bajo carga, generando timeouts difíciles de diagnosticar.
Aplicar migraciones generadas por TypeORM sin revisar el SQL puede generar cambios destructivos no intencionados como drops de columnas. Se espera conocer que las migraciones automáticas deben revisarse siempre antes de aplicarse en producción.