Saltar al contenido principal

Migración a v1

Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Esta es la guía de migración para actualizar desde la versión 0.3.x a la 1.0.

Requisitos de la plataforma

Node.js 20+

El objetivo mínimo de JavaScript ahora es ES2023, lo que requiere Node.js 20 o superior. Si estás usando una versión anterior de Node.js, actualízala antes de actualizar TypeORM.

Buffer reemplazado por Uint8Array en plataformas que no son Node

Se ha eliminado el polyfill de Buffer para navegador. En plataformas que no son Node (navegador, Deno, Bun), los datos binarios ahora se representan como Uint8Array. Los usuarios de Node.js no se ven afectados — el Buffer de Node extiende Uint8Array y sigue funcionando como antes.

Cambios en los drivers

MySQL / MariaDB

Opción connectorPackage eliminada

La opción connectorPackage fue eliminada, junto con el soporte para el antiguo cliente mysql. El único cliente de base de datos admitido ahora es mysql2, que TypeORM cargará por defecto. Si usabas mysql en tu proyecto, simplemente sustitúyelo por mysql2.

Valor predeterminado de legacySpatialSupport cambiado a false

La opción legacySpatialSupport ahora tiene como valor predeterminado false, lo que significa que TypeORM usa las funciones espaciales compatibles con el estándar ST_GeomFromText y ST_AsText introducidas en MySQL 5.7 y requeridas por MySQL 8.0+. Las funciones heredadas GeomFromText y AsText fueron eliminadas en MySQL 8.0.

Si estás usando MySQL 5.6 o anterior y dependes de tipos espaciales, establece explícitamente legacySpatialSupport: true:

new DataSource({
type: "mysql",
legacySpatialSupport: true,
// ...
})

Opciones de columna width y zerofill eliminadas

MySQL 8.0.17 marcó como obsoleta la anchura de visualización para tipos enteros y el atributo ZEROFILL, y MySQL 8.4 las eliminó por completo. TypeORM ya no admite las opciones de columna width y zerofill. Si usabas estas opciones, elimínalas de tus definiciones de columnas:

// Before
@Column({ type: "int", width: 9, zerofill: true })
postCode: number

// After
@Column({ type: "int" })
postCode: number

Si necesitas formato de visualización con relleno de ceros, manéjalo en tu capa de aplicación usando String.prototype.padStart() o la función LPAD() de MySQL en una consulta en bruto. La opción unsigned para tipos enteros no se ve afectada por este cambio.

SQLite

El paquete sqlite3 ha sido descartado. Usa better-sqlite3 en su lugar:

// Before
new DataSource({
type: "sqlite",
database: "db.sqlite",
})

// After
new DataSource({
type: "better-sqlite3",
database: "db.sqlite",
})

Opción flags eliminada

El paquete sqlite3 aceptaba flags de apertura a nivel C (OPEN_URI, OPEN_SHAREDCACHE, etc.). better-sqlite3 no soporta esto — usa estas opciones dedicadas en su lugar:

  • readonly para modo de solo lectura

  • enableWAL para modo de journal WAL

Opción busyTimeout renombrada a timeout

El paquete sqlite3 usaba busyTimeout para configurar el tiempo de espera de ocupado de SQLite. better-sqlite3 usa timeout en su lugar (predeterminado: 5000ms):

// Before
new DataSource({
type: "sqlite",
database: "db.sqlite",
busyTimeout: 2000,
})

// After
new DataSource({
type: "better-sqlite3",
database: "db.sqlite",
timeout: 2000,
})

MongoDB

TypeORM ahora requiere el driver de Node.js mongodb v7 o superior (^7.0.0). Se eliminó el soporte para el driver mongodb v5/v6.

Opciones de conexión obsoletas eliminadas

Se eliminaron las siguientes opciones de conexión para MongoDB:

Removed optionAction
appnameUse appName (camelCase) instead
fsyncUse writeConcern: { journal: true } instead
jUse writeConcern: { journal: true } instead
keepAliveRemove — always enabled since MongoDB Driver v6.0
keepAliveInitialDelayRemove — not configurable since MongoDB Driver v6.0
sslUse tls instead
sslCAUse tlsCAFile instead
sslCRLRemove — no replacement in modern driver
sslCertUse tlsCertificateKeyFile instead
sslKeyUse tlsCertificateKeyFile instead
sslPassUse tlsCertificateKeyFilePassword instead
sslValidateUse tlsAllowInvalidCertificates (inverted) instead
useNewUrlParserRemove — no-op since MongoDB Driver v4.0
useUnifiedTopologyRemove — no-op since MongoDB Driver v4.0
wUse writeConcern: { w: 1 } instead
wtimeoutUse writeConcern: { wtimeoutMS: 2500 } instead
wtimeoutMSUse writeConcern: { wtimeoutMS: 2500 } instead

Método stats() eliminado

Se eliminó el método stats() de MongoQueryRunner, MongoEntityManager y MongoRepository. El comando subyacente collStats quedó obsoleto en MongoDB server 6.2 y el método Collection.stats() fue eliminado en MongoDB Driver v7.

Usa la etapa de agregación $collStats en su lugar. Ten en cuenta que la estructura de la respuesta es diferente: propiedades como count, size y storageSize están anidadas bajo storageStats en lugar de estar en el nivel superior.

// Before
const stats = await mongoRepository.stats()
console.log(stats.count)
console.log(stats.size)
console.log(stats.totalIndexSize)

// After — use $collStats aggregation stage
const [stats] = await dataSource.mongoManager
.aggregate(MyEntity, [{ $collStats: { storageStats: {} } }])
.toArray()
console.log(stats.storageStats.count)
console.log(stats.storageStats.size)
console.log(stats.storageStats.totalIndexSize)

Globales getMongoRepository y getMongoManager eliminados

Se eliminaron las funciones globales obsoletas getMongoRepository() y getMongoManager(). Usa los métodos correspondientes en instancias de DataSource o EntityManager en su lugar:

// Before
import { getMongoManager, getMongoRepository } from "typeorm"

const manager = getMongoManager()
const repository = getMongoRepository(User)

// After
const manager = dataSource.mongoManager
const repository = dataSource.getMongoRepository(User)

Tipos

Los tipos internos de MongoDB (ObjectId, etc.) ya no se reexportan desde typeorm. Impórtalos directamente desde mongodb:

// Before
import { ObjectId } from "typeorm"

// After
import { ObjectId } from "mongodb"

MS SQL Server

Opción de conexión domain eliminada

La opción obsoleta domain en SqlServerConnectionCredentialsOptions ha sido eliminada. Usa la opción authentication con tipo NTLM en su lugar:

// Before
new DataSource({
type: "mssql",
domain: "MYDOMAIN",
username: "user",
password: "pass",
// ...
})

// After
new DataSource({
type: "mssql",
authentication: {
type: "ntlm",
options: {
domain: "MYDOMAIN",
userName: "user",
password: "pass",
},
},
// ...
})

SAP HANA

Se eliminaron varios alias obsoletos para conexiones SAP HANA.

  • hanaClientDriver fue eliminado. Usa driver.

  • pool.max fue eliminado. Usa pool.maxConnectedOrPooled.

  • pool.requestTimeout fue eliminado. Usa pool.maxWaitTimeoutIfPoolExhausted.

  • pool.idleTimeout fue eliminado. Usa pool.maxPooledIdleTime (en segundos).

  • pool.min, pool.maxWaitingRequests y pool.checkInterval fueron eliminados sin reemplazo.

También ten en cuenta los cambios en el comportamiento predeterminado de la configuración del pool:

  • pool.maxPooledIdleTime ahora tiene como valor predeterminado 30 segundos y ya no recurre a pool.idleTimeout.

  • pool.maxWaitTimeoutIfPoolExhausted ahora tiene como valor predeterminado 0 y ya no recurre a pool.requestTimeout.

Expo

Se ha eliminado el soporte para el driver SQLite heredado de Expo. La API heredada fue eliminada por Expo en el SDK v52. Actualiza a Expo SDK v52 o superior y usa la API asíncrona moderna de SQLite:

// Before
new DataSource({
type: "expo",
database: "db.sqlite",
})

// After — use Expo SDK v52+ with the modern async API
new DataSource({
type: "expo",
database: "db.sqlite",
driver: require("expo-sqlite"),
})

Redis (caché)

Se eliminó el soporte para clientes Redis heredados (v3) en RedisQueryResultCache. Actualiza a Redis client v4 o superior (redis, ioredis):

// Before — redis v3
import { createClient } from "redis"
const client = createClient()

// After — redis v4+
import { createClient } from "redis"
const client = createClient()
await client.connect()

Data Source

ConnectionDataSource

DataSource reemplazó a Connection en v0.3. El alias compatible con versiones anteriores ahora ha sido eliminado:

// Before
import { Connection, ConnectionOptions } from "typeorm"

const connection = await createConnection(options)
await connection.close()

// After
import { DataSource, DataSourceOptions } from "typeorm"

const dataSource = new DataSource(options)
await dataSource.initialize()
await dataSource.destroy()

Los siguientes cambios de nombre se aplican en todo:

BeforeAfter
ConnectionDataSource
ConnectionOptionsDataSourceOptions
BaseConnectionOptionsBaseDataSourceOptions
MysqlConnectionOptionsMysqlDataSourceOptions
(same pattern for all drivers)
connection.connect()dataSource.initialize()
connection.close()dataSource.destroy()
connection.isConnecteddataSource.isInitialized

Propiedad name eliminada

La propiedad obsoleta name en DataSource y BaseDataSourceOptions ha sido eliminada. Las conexiones nombradas se marcaron como obsoletas en v0.3 cuando se eliminó ConnectionManager. Si usabas name para identificar conexiones, gestiona tus instancias de DataSource directamente.

Nota: el código que lee dataSource.name ahora recibirá undefined en lugar de "default". Si usas este valor en registros o lógica multi-tenant, actualízalo en consecuencia.

La propiedad .connection en varias clases ahora es .dataSource

La propiedad connection en las clases Driver, QueryRunner, EntityManager, QueryBuilder, EntityMetadata y *Event se renombró a dataSource. Para EntityManager, este cambio se anunció en la versión 0.3, pero no se implementó realmente. Para facilitar la transición, se agregó un getter obsoleto que devuelve el mismo valor que dataSource.

Varios

La clase ConnectionManager ha sido eliminada. Si la usabas para gestionar múltiples conexiones, crea y gestiona tus instancias de DataSource directamente en su lugar.

ConnectionOptionsReader también ha sido simplificado: all() se renombró a get() (devuelve todas las configuraciones como arreglo), y los métodos antiguos get(name) y has(name) fueron eliminados.

const reader = new ConnectionOptionsReader()

// when your ormconfig has a single data source
const [options] = await reader.get()

// when you need a specific config from multiple data sources
const allOptions = await reader.get()
const postgresOptions = allOptions.find((o) => o.type === "postgres")

Funciones globales de conveniencia eliminadas

Las siguientes funciones globales obsoletas han sido eliminadas:

  • createConnection / createConnections

  • getConnection

  • getConnectionManager

  • getConnectionOptions

  • getManager

  • getSqljsManager

  • getRepository

  • getTreeRepository

  • createQueryBuilder

Usa los métodos equivalentes en tu instancia de DataSource:

// Before
const repo = getRepository(User)
const qb = createQueryBuilder("user")

// After
const repo = dataSource.getRepository(User)
const qb = dataSource.createQueryBuilder("user")

Eliminada la configuración mediante variables de entorno

La clase obsoleta ConnectionOptionsEnvReader y la capacidad de configurar conexiones mediante variables de entorno TYPEORM_CONNECTION, TYPEORM_URL y otras TYPEORM_* han sido eliminadas. El formato de archivo ormconfig.env tampoco es soportado. TypeORM ya no carga automáticamente archivos .env ni depende de dotenv.

Utiliza un archivo de configuración TypeScript o JavaScript en su lugar:

// ormconfig.ts
export default {
type: process.env.DB_TYPE,
url: process.env.DB_URL,
// ...
}

Cambios de comportamiento

Comportamiento predeterminado de invalidWhereValuesBehavior cambiado a throw

Este es un cambio de comportamiento significativo que podría romper aplicaciones existentes en tiempo de ejecución.

El comportamiento predeterminado para valores nulos e indefinidos en condiciones WHERE ha cambiado. Anteriormente, estos valores se ignoraban silenciosamente (la propiedad se omitía). Ahora, ambos lanzan un error por defecto.

Este cambio evita errores sutiles donde consultas como findBy({ id: undefined }) devolvían silenciosamente todas las filas en lugar de fallar.

// v0.3: silently returns all posts (null is ignored)
// v1.0: throws TypeORMError
await repository.find({ where: { text: null } })

// v0.3: silently returns all posts (undefined is ignored)
// v1.0: throws TypeORMError
await repository.find({ where: { text: undefined } })

Para buscar valores nulos, usa el operador IsNull():

import { IsNull } from "typeorm"

await repository.find({ where: { text: IsNull() } })

Para restaurar el comportamiento anterior, configura invalidWhereValuesBehavior en las opciones de tu fuente de datos:

new DataSource({
// ...
invalidWhereValuesBehavior: {
null: "ignore",
undefined: "ignore",
},
})

Esta configuración protege todas las APIs de alto nivel — operaciones de búsqueda, métodos de mutación de repositorio/manager, y queryBuilder.setFindOptions() (el único método de QueryBuilder afectado). El resto de métodos de QueryBuilder (.where(), .andWhere(), .orWhere()) no están afectados — los valores nulos e indefinidos pasan directamente. Consulta Manejo de nulos e indefinidos para detalles completos.

Hashing

TypeORM usaba anteriormente el paquete npm sha.js para hashing SHA-1 (una implementación no estándar). Esto ha sido reemplazado por el módulo crypto integrado en Node.js, y el paquete uuid ha sido sustituido por crypto.randomUUID().

Para entornos de navegador, RandomGenerator.sha1 se corrigió para usar la implementación estándar.

Impacto: Si usas la caché de resultados de consultas de TypeORM, las entradas en caché existentes se invalidarán después de actualizar porque la función hash produce una salida diferente. Esto es inofensivo — las cachés se reconstruirán automáticamente — pero podrías ver un aumento temporal en fallos de caché.

Patrones glob

Los patrones glob (usados en la detección de archivos de entidades/migraciones) ahora son manejados por tinyglobby en lugar de glob. Esto es mayormente compatible, pero casos extremos con expansión de llaves o separadores de ruta específicos de plataforma pueden comportarse diferente.

Columnas

Eliminada la opción readonly

La opción obsoleta readonly para columnas ha sido eliminada. Usa la opción update en su lugar — ten en cuenta que toma el valor opuesto:

// Before
@Column({ readonly: true })
authorName: string

// After
@Column({ update: false })
authorName: string

Eliminado unsigned en ColumnNumericOptions

La propiedad obsoleta unsigned en ColumnNumericOptions (usada con sobrecargas de tipo decimal/float como @Column("decimal", { unsigned: true })) ha sido eliminada, ya que MySQL marcó como obsoleto UNSIGNED para tipos numéricos no enteros. La opción unsigned en ColumnOptions para tipos enteros no está afectada.

Relaciones

nullable: false ahora usa INNER JOIN

Las relaciones marcadas con nullable: false ahora usan INNER JOIN en lugar de LEFT JOIN cuando se cargan mediante relations, carga eager u opciones de búsqueda. Esto aplica solo para tipos de relaciones que poseen la columna de unión (ManyToOne y OneToOne del lado propietario).

Esto es semánticamente correcto porque una clave foránea no nula garantiza que la entidad relacionada existe, y permite al optimizador de la base de datos generar planes de consulta más eficientes.

Posible cambio disruptivo: Si tu base de datos contiene filas que violan la restricción NOT NULL (ej: claves foráneas huérfanas, o nullable: false estaba configurado pero la columna es realmente nullable en la BD), esas filas serán excluidas de los resultados. Verifica la integridad de tus datos o cambia la relación a nullable: true si es necesario.

// INNER JOIN — related entity is guaranteed to exist
@ManyToOne(() => User, { nullable: false })
author: User

// LEFT JOIN — related entity may not exist (default)
@ManyToOne(() => User)
optionalEditor: User

Las relaciones OneToMany, ManyToMany e inversas OneToOne siempre usan LEFT JOIN independientemente de la configuración nullable, ya que estos tipos de relación no tienen una columna de unión en la tabla actual.

Excepción de soft-delete: Si la entidad relacionada tiene una @DeleteDateColumn, se usa LEFT JOIN incluso para relaciones nullable: false (a menos que se establezca withDeleted: true). Esto evita que entidades relacionadas eliminadas suavemente filtren sus filas padre.

Repositorio

findOneById

El método obsoleto findOneById ha sido eliminado de EntityManager, Repository, BaseEntity, MongoEntityManager y MongoRepository. Usa findOneBy en su lugar:

// Before
const user = await manager.findOneById(User, 1)
const user = await repository.findOneById(1)
const user = await User.findOneById(1)

// After
const user = await manager.findOneBy(User, { id: 1 })
const user = await repository.findOneBy({ id: 1 })
const user = await User.findOneBy({ id: 1 })

Para entidades MongoDB con @ObjectIdColumn(), findOneBy funciona igual: TypeORM traduce automáticamente el nombre de propiedad a _id.

Eliminado findByIds

El método obsoleto findByIds ha sido eliminado de EntityManager, Repository y BaseEntity. Usa findBy con el operador In en su lugar:

// Before
const users = await repository.findByIds([1, 2, 3])

// After
import { In } from "typeorm"

const users = await repository.findBy({ id: In([1, 2, 3]) })

exist renombrado a exists

El método obsoleto Repository.exist() ha sido eliminado. Usa exists() en su lugar — el comportamiento es idéntico:

// Before
const hasUsers = await userRepository.exist({ where: { isActive: true } })

// After
const hasUsers = await userRepository.exists({ where: { isActive: true } })

Eliminados AbstractRepository, @EntityRepository y getCustomRepository

La clase AbstractRepository, el decorador @EntityRepository y el método getCustomRepository() han sido eliminados. Estas funcionalidades se marcaron como obsoletas en v0.3 en favor de Repository.extend():

// Before
@EntityRepository(User)
class UserRepository extends AbstractRepository<User> {
findByName(name: string) {
return this.repository.findOneBy({ name })
}
}
const userRepo = dataSource.getCustomRepository(UserRepository)

// After
const UserRepository = dataSource.getRepository(User).extend({
findByName(name: string) {
return this.findOneBy({ name })
},
})

Las siguientes clases de error también fueron eliminadas: CustomRepositoryDoesNotHaveEntityError, CustomRepositoryCannotInheritRepositoryError, CustomRepositoryNotFoundError.

Eliminados el decorador @RelationCount y loadRelationCountAndMap

El decorador @RelationCount y el método SelectQueryBuilder.loadRelationCountAndMap() han sido eliminados. Usa @VirtualColumn o una subconsulta en tu constructor de consultas en su lugar:

// Before
@RelationCount((post: Post) => post.categories)
categoryCount: number

// After — use @VirtualColumn with a sub-query
// Replace the junction table name and column names to match your schema
@VirtualColumn({
query: (alias) =>
`SELECT COUNT(*) FROM post_categories_category WHERE postId = ${alias}.id`,
})
categoryCount: number

Opciones de Búsqueda

Opción join eliminada

La propiedad obsoleta join en FindOneOptions y FindManyOptions ha sido eliminada, junto con la interfaz JoinOptions.

leftJoinAndSelectrelations

Si usabas leftJoinAndSelect, sustitúyelo con la sintaxis de objeto relations: relations siempre realiza LEFT JOINs con selección, lo cual es equivalente:

// Before
const posts = await repository.find({
join: {
alias: "post",
leftJoinAndSelect: {
categories: "post.categories",
author: "post.author",
},
},
})

// After
const posts = await repository.find({
relations: { categories: true, author: true },
})

Otros tipos de join → QueryBuilder

La opción relations solo soporta LEFT JOINs con selección. Si usabas innerJoinAndSelect, innerJoin o leftJoin (sin select), cambia a la API de QueryBuilder:

// Before — innerJoinAndSelect
const posts = await repository.find({
join: {
alias: "post",
innerJoinAndSelect: {
categories: "post.categories",
},
},
})

// After — QueryBuilder with innerJoinAndSelect
const posts = await repository
.createQueryBuilder("post")
.innerJoinAndSelect("post.categories", "categories")
.getMany()

// Before — leftJoin (without select)
const posts = await repository.find({
join: {
alias: "post",
leftJoin: {
categories: "post.categories",
},
},
where: { categories: { isRemoved: false } },
})

// After — QueryBuilder with leftJoin
const posts = await repository
.createQueryBuilder("post")
.leftJoin("post.categories", "categories")
.where("categories.isRemoved = :isRemoved", { isRemoved: false })
.getMany()

Esta distinción es importante en la práctica. Por ejemplo, PostgreSQL y CockroachDB no permiten FOR UPDATE en el lado nullable de un outer join, por lo que consultas que combinan bloqueo con relaciones unidas pueden requerir INNER JOINs:

// Before — innerJoinAndSelect + lock
const post = await repository.findOne({
join: {
alias: "post",
innerJoinAndSelect: {
categories: "post.categories",
},
},
lock: { mode: "pessimistic_write", tables: ["category"] },
})

// After — QueryBuilder with innerJoinAndSelect + lock
const post = await repository
.createQueryBuilder("post")
.innerJoinAndSelect("post.categories", "categories")
.setLock("pessimistic_write", undefined, ["categories"])
.getOne()

Bloqueo con relaciones anidadas → QueryBuilder

La opción relations no se puede usar con bloqueo pesimista en tablas unidas porque relations siempre utiliza LEFT JOINs, y PostgreSQL/CockroachDB rechazan FOR UPDATE en el lado anulable de las uniones externas. Usa QueryBuilder con innerJoinAndSelect en su lugar:

// Before — nested relations + lock via find options
const post = await repository.findOne({
where: { id: 1 },
join: {
alias: "post",
innerJoinAndSelect: {
categories: "post.categories",
images: "categories.images",
},
},
lock: { mode: "pessimistic_write", tables: ["images"] },
})

// After — QueryBuilder with innerJoinAndSelect + lock
const post = await repository
.createQueryBuilder("post")
.innerJoinAndSelect("post.categories", "categories")
.innerJoinAndSelect("categories.images", "images")
.where("post.id = :id", { id: 1 })
.setLock("pessimistic_write", undefined, ["images"])
.getOne()

Ten en cuenta que el bloqueo de la tabla principal aún funciona con relations — solo el bloqueo de tablas unidas requiere QueryBuilder con uniones internas.

select basado en cadenas eliminado

Se ha eliminado la sintaxis obsoleta de matriz de cadenas para las opciones de búsqueda select. Usa la sintaxis de objeto en su lugar:

// Before
const users = await repository.find({
select: ["id", "name"],
})

// After
const users = await repository.find({
select: { id: true, name: true },
})

El tipo eliminado es FindOptionsSelectByString.

relations basado en cadenas eliminado

Se ha eliminado la sintaxis obsoleta de matriz de cadenas para las opciones de búsqueda relations. Usa la sintaxis de objeto en su lugar:

// Before
const users = await repository.find({
relations: ["profile", "posts"],
})

// After
const users = await repository.find({
relations: { profile: true, posts: true },
})

El tipo eliminado es FindOptionsRelationByString.

QueryBuilder

printSql eliminado

Se eliminó el método printSql() de los query builders. Era redundante porque todas las consultas ejecutadas ya se registran automáticamente mediante el logger configurado cuando el registro de consultas está activado. Usa getSql() o getQueryAndParameters() para inspeccionar el SQL generado:

// Before
const users = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id", { id: 1 })
.printSql()
.getMany()

// After — inspect SQL before executing
const qb = dataSource
.getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id", { id: 1 })

console.log(qb.getSql())
// or: const [sql, params] = qb.getQueryAndParameters()

const users = await qb.getMany()

Para registrar automáticamente todas las consultas ejecutadas, activa el registro de consultas en tu DataSource:

new DataSource({
// ...
logging: ["query"],
})

Eliminado onConflict

El método onConflict() en InsertQueryBuilder ha sido eliminado. Aceptaba cadenas SQL crudas que eran específicas del controlador y propensas a errores. Usa orIgnore() u orUpdate() en su lugar:

// Before
await dataSource
.createQueryBuilder()
.insert()
.into(Post)
.values(post)
.onConflict(`("id") DO NOTHING`)
.execute()

// After
await dataSource
.createQueryBuilder()
.insert()
.into(Post)
.values(post)
.orIgnore()
.execute()

// Before
await dataSource
.createQueryBuilder()
.insert()
.into(Post)
.values(post)
.onConflict(`("id") DO UPDATE SET "title" = :title`)
.setParameter("title", post.title)
.execute()

// After
await dataSource
.createQueryBuilder()
.insert()
.into(Post)
.values(post)
.orUpdate(["title"], ["id"])
.execute()

Eliminada la sobrecarga de objeto en orUpdate

La sobrecarga basada en objetos orUpdate() que acepta { columns?, overwrite?, conflict_target? } ha sido eliminada. Utiliza la firma basada en arreglos en su lugar:

// Before
.orUpdate({ conflict_target: ["date"], overwrite: ["title"] })

// After
.orUpdate(["title"], ["date"])

Eliminado setNativeParameters

// Before
qb.setNativeParameters({ key: "value" })

// After
qb.setParameters({ key: "value" })

La propiedad interna QueryExpressionMap.nativeParameters también ha sido eliminada. Si tienes una subclase personalizada de QueryBuilder que accede a expressionMap.nativeParameters, cambia a expressionMap.parameters.

Eliminado el alias de tipo WhereExpression

// Before
import { WhereExpression } from "typeorm"

// After
import { WhereExpressionBuilder } from "typeorm"

Eliminado replacePropertyNames

El método protegido obsoleto replacePropertyNames() ha sido eliminado. Ya no tenía efecto desde que el reemplazo de nombres de propiedades se movió al procesamiento final de consultas mediante replacePropertyNamesForTheWholeQuery(). Si estabas sobrescribiendo este método en una subclase personalizada de QueryBuilder, tu sobrescritura ya no se invocará.

Eliminados modos de bloqueo obsoletos

// Before
.setLock("pessimistic_partial_write")

// After
.setLock("pessimistic_write")
.setOnLocked("skip_locked")

// Before
.setLock("pessimistic_write_or_fail")

// After
.setLock("pessimistic_write")
.setOnLocked("nowait")

Lo mismo aplica para opciones de búsqueda:

// Before
{ lock: { mode: "pessimistic_partial_write" } }

// After
{ lock: { mode: "pessimistic_write", onLocked: "skip_locked" } }

// Before
{ lock: { mode: "pessimistic_write_or_fail" } }

// After
{ lock: { mode: "pessimistic_write", onLocked: "nowait" } }

Migraciones

Eliminado getAllMigrations

El método obsoleto getAllMigrations() ha sido eliminado de MigrationExecutor. Usa getPendingMigrations() o getExecutedMigrations() en su lugar, o accede directamente a dataSource.migrations para obtener la lista de clases de migración registradas:

// Before
const migrations = await migrationExecutor.getAllMigrations()

// After — depending on what you need
const pending = await migrationExecutor.getPendingMigrations()
const executed = await migrationExecutor.getExecutedMigrations()
const registered = dataSource.migrations

Eliminados QueryRunner.loadedTables y loadedViews

// Before
const tables = queryRunner.loadedTables
const views = queryRunner.loadedViews

// After
const tables = await queryRunner.getTables()
const views = await queryRunner.getViews()

Nota: Los reemplazos son métodos asíncronos, no propiedades síncronas.

Sistema de contenedores

Se ha eliminado la integración obsoleta de contenedores IoC: useContainer(), getFromContainer(), ContainerInterface, ContainedType y UseContainerOptions.

TypeORM ya no tiene soporte integrado para contenedores IoC. Los paquetes typeorm-typedi-extensions y typeorm-routing-controllers-extensions tampoco son compatibles. Las siguientes secciones explican cómo migrar según tu configuración.

Suscriptores y migraciones con dependencias

TypeORM siempre instancia suscriptores y migraciones internamente usando un constructor sin argumentos, por lo que no puedes pasar instancias preconstruidas. Si tus migraciones necesitan acceder a servicios, usa el DataSource (disponible vía queryRunner.dataSource) dentro de la migración misma:

// Before
import { useContainer } from "typeorm"
import { Container } from "typedi"
useContainer(Container)

// After — access dependencies via the DataSource inside the migration
export class MyMigration1234 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
const repo = queryRunner.dataSource.getRepository(User)
// ...
}
}

Acceso a repositorios y al gestor de entidades

Si antes usabas typeorm-typedi-extensions para inyectar EntityManager o repositorios en tus servicios, usa el DataSource directamente en su lugar:

// Before (with typeorm-typedi-extensions)
import { InjectManager, InjectRepository } from "typeorm-typedi-extensions"

class UserService {
@InjectManager()
private manager: EntityManager

@InjectRepository(User)
private userRepository: Repository<User>
}

// After — access from the DataSource instance
class UserService {
private manager: EntityManager
private userRepository: Repository<User>

constructor(dataSource: DataSource) {
this.manager = dataSource.manager
this.userRepository = dataSource.getRepository(User)
}
}

Uso con un framework DI

Si usas un framework DI, registra el DataSource (o sus repositorios) como proveedores en tu contenedor:

// typedi example
import { DataSource } from "typeorm"
import { Container } from "typedi"

const dataSource = new DataSource({
/* ... */
})
await dataSource.initialize()
Container.set(DataSource, dataSource)
Container.set("UserRepository", dataSource.getRepository(User))

NestJS

Los usuarios de NestJS no están afectados — el paquete @nestjs/typeorm tiene su propia integración que no depende del sistema de contenedores eliminado en TypeORM. Sin embargo, @nestjs/typeorm v10 y la versión actual v11.0.0 intentan registrar la clase eliminada Connection y fallarán al iniciar. Asegúrate de usar una versión de @nestjs/typeorm que incluya la corrección para compatibilidad con TypeORM v1.

Otras eliminaciones internas

Se han eliminado las siguientes API internas. Esto solo te afectará si estabas construyendo drivers personalizados, extendiendo QueryBuilder o usando API de metadatos de bajo nivel:

RemovedReplacement
EntityMetadata.createPropertyPath() (static)Removed with no public replacement
DriverUtils.buildColumnAlias()Use DriverUtils.buildAlias()
Broadcaster.broadcastLoadEventsForAll()No replacement — use individual event subscribers
QueryExpressionMap.nativeParametersUse QueryExpressionMap.parameters