Upgrade guide
Only releases that may require something on your side. Everything else, and what a custom dialect or driver sees, is in the changelog.
0.77.1 - 2026-09-20
Section titled “0.77.1 - 2026-09-20”Rename UqlLockUsageError to UqlUsageError, which every misuse now throws: the old name still resolves
to it, so an instanceof keeps working. A call the API cannot carry out answers 400 over HTTP where it
answered 500.
0.73.0 - 2026-09-18
Section titled “0.73.0 - 2026-09-18”Rename WithDistance and WithScore to WithProjection, which types the row any $sort $project
names. On SQLite, libSQL and Turso, a vector column created before 0.71.0 is TEXT, which drift:check
now reports: recreate its table in a migration with the column as F32_BLOB, which libSQL’s vector index
needs to build.
0.70.0 - 2026-09-18
Section titled “0.70.0 - 2026-09-18”Run the codemod: a nullable column’s property admits null, name?: string | null, so it
appends the | null. Give a column that never holds one nullable: false instead. An updateMany or
deleteMany naming no rows throws; pass { unfiltered: true } where you mean the whole table. A write
payload naming a readonly field no longer compiles, since its value was dropped. Replace $sumDistinct
and $avgDistinct with a $group on the field.
0.68.1 - 2026-09-17
Section titled “0.68.1 - 2026-09-17”An RPC contract takes WireQuery<E>, a raw or a
Uint8Array in a browser-client query or payload no longer compiles, and a cast past the types throws
rather than corrupting the row: keep both server-side. Run the codemod for D1Database, now
D1Queryable. A query type names its raw before its key set, QueryWhere<E, Raw, K>.
0.68.0 - 2026-09-17
Section titled “0.68.0 - 2026-09-17”On PostgreSQL and CockroachDB, generate a migration for a numeric jsonPath index: its
expression changed, and the planner no longer matches the old one. An $elemMatch on one $eq or $in
compares by JSON type, so '5' stops matching 5, and an object in $all matches an element holding its
keys rather than only an identical one.
0.66.0 - 2026-09-16
Section titled “0.66.0 - 2026-09-16”Run the codemod: every to-one names its foreign key in references, and declares the column
where a relation used to create it. For a to-one onto a composite key, declare a column per key and pair
them. Then tsc points at what registration used to refuse at startup: a security filter that skips, and a
mappedBy or references naming a column that cannot hold the key it joins.
0.65.0 - 2026-09-14
Section titled “0.65.0 - 2026-09-14”Run the codemod, then give each junction column references, declare a relation for any
@Field({ references }) column you $populate, and read pool.dialect.dialectName instead of
migrator.dialectName.
0.63.0 - 2026-09-13
Section titled “0.63.0 - 2026-09-13”Write an index expression as raw in the list, (user) => [raw`lower(${user.email})`], not a callback per
expression. Move SQLite off uql-orm/bunSql to Sqlite3QuerierPool, upgrade
@tursodatabase/serverless to 1.3+, and pass a libSQL client you built to LibsqlQuerierPool.
SQLite now reads an integer past 2^53 as text.
0.62.0 - 2026-09-13
Section titled “0.62.0 - 2026-09-13”Run the codemod: it rewrites the renamed options and col(), now refs(Entity).
SQL an entity declares reads its columns off a callback’s refs, (user) => raw`lower(${user.email})` .
0.60.0 - 2026-09-12
Section titled “0.60.0 - 2026-09-12”A builder migration receives the builder, with the querier second: up(m, querier).
One written to call querier.run there now reaches the builder instead; move it to defineMigration.
0.58.0 - 2026-09-11
Section titled “0.58.0 - 2026-09-11”A member is never named by a string, so a rename in your editor reaches it. Definitions read it off a key
map: @Index((post) => [post.title]), mappedBy: (post) => post.author, references. Statements name it by
a key: aggregate() takes its computed columns in $select instead of $agg, and $text takes
$fields: { title: true }. The codemod rewrites all of it.
await pool.aggregate(Order, { $group: { status: true }, $select: { total: { $sum: { amount: true } } },});0.57.0 - 2026-09-11
Section titled “0.57.0 - 2026-09-11”A to-many $populate and $count are read in the parent’s statement, which needs MySQL 8.0.14+ and SQLite
3.44+. Each table in a statement reads under its own name, relation key or join path, so a raw() naming a
related table has to use that alias. Every foreign key gets an index unless one already leads with it
(index: false opts a column out), so the next migration adds them.
0.55.0 - 2026-09-10
Section titled “0.55.0 - 2026-09-10”A BIGINT past 2^53 reads back as its exact text, where most drivers rounded it; declare type: BigInt for a
typed exact integer. The codemod renames CrdbQuerier/NeonQuerier to PgQuerier and
LibsqlQuerier/TursoQuerier to HranaQuerier. Raw access on Bun SQL is pool.sql.
0.54.0 - 2026-09-10
Section titled “0.54.0 - 2026-09-10”virtual and raw('sql') are gone; the codemod rewrites both. An after* hook’s this is now a
copy of the row as written, so mutating it leaves the object you passed alone.
0.53.0 - 2026-09-10
Section titled “0.53.0 - 2026-09-10”$where takes a map and nothing else, so TypeScript reports a wrong value on the key it sits on. An id, a
list of ids or a bare raw() in its place stops compiling, and over HTTP answers 400. Name the key, and put
a raw expression in $and:
import { raw } from 'uql-orm';
await pool.findMany(User, { $where: { id: [1, 2] } });await pool.findMany(User, { $where: { $and: [raw`"createdAt" > now()`] } });The codemod renames QueryWhereMap to QueryWhere and reports the rest.
0.52.0 - 2026-09-09
Section titled “0.52.0 - 2026-09-09”The pool builds the engine’s own dialect, so BunSqlPostgresDialect, BunSqlCockroachDialect and
BunSqliteDialect are gone.
0.48.0 to 0.51.0 - 2026-09-09
Section titled “0.48.0 to 0.51.0 - 2026-09-09”Four releases in one day, all about how a write names its rows.
saveOne/saveMany upsert on the key a row names, instead of guessing from whether an id is present. A
stale id writes the row now rather than updating nothing, and composite keys
work. A row that names its key fires @BeforeUpsert/@AfterUpsert, so
move whatever a @BeforeUpdate was doing on a save.
Every write reports its id in one shape: the column’s value on a single key, the key
map on a composite, where a composite insert used to report undefined.
firstId is gone from upsertOne and upsertMany, replaced by id and by ids in payload order.
A key not called id, _id or uuid, and a composite whatever its columns are called, has to be named by
the idKey brand, which the codemod writes. @Id refuses one without it:
import { Entity, Id, idKey } from 'uql-orm';
@Entity()export class Enrolment { [idKey]?: 'studentId' | 'courseId';
@Id({ type: Number }) studentId?: number; @Id({ type: String }) courseId?: string;}On MongoDB, a key you supply is the document’s _id, where it used to land beside the one the driver minted
so findOneById never found the row; documents written before this keep the minted id. Ids read back as hex
strings, and a key left to the database has to be one MongoDB can
mint.
0.46.0 - 2026-09-08
Section titled “0.46.0 - 2026-09-08”virtual is renamed to computed. Both work for one release, giving both
throws, and the codemod rewrites it.
0.45.0 - 2026-09-08
Section titled “0.45.0 - 2026-09-08”A sync now applies foreign keys, so read a planSync() before the first one; not on SQLite, whose only way
to change a constraint is rebuilding the table. A generated key is spelled from the type it declares, so on
MySQL and MariaDB it is no longer UNSIGNED, and a key left UNSIGNED refuses every constraint pointing at
it:
-- on a database created before this, under safe: falseALTER TABLE `Company` MODIFY COLUMN `id` BIGINT AUTO_INCREMENT;Run it before adding foreign keys to those tables, as a migration rather than at boot, since it rebuilds the
table under a metadata lock. A key filled by onInsert is no longer auto-increment, and
serial, bigserial and smallserial are gone as columnType: declare the width, @Id({ type: Number })
or @Id({ type: Number, columnType: 'int' }).
0.44.0 - 2026-09-07
Section titled “0.44.0 - 2026-09-07”A naming strategy no longer rewrites a table you named yourself, so check which table
@Entity({ name: 'UserProfile' }) entities now map to; ones that name no table are unaffected.
migrator.autoSync, syncForce and syncEntity are one migrator.sync(options).
0.43.0 - 2026-09-07
Section titled “0.43.0 - 2026-09-07”An option a column cannot use is now an error where it used to be silently ignored. Delete length,
precision, scale, autoIncrement, dimensions and distance where the column’s type has no use for
them, any DDL or generator option on a virtual field, onUpdate beside updatable: false,
nullable: true on a key, and a defaultValue that is not the value the column holds (a JSON column keeps
the SQL literal it stores, defaultValue: '{}').
0.42.1 - 2026-09-05
Section titled “0.42.1 - 2026-09-05”Nothing in your database is renamed or rewritten: keys and indexes are recognised by the columns they cover, never by name. Adding a key column to a table that already has rows fails until you fill it in.
0.42.0 - 2026-09-04
Section titled “0.42.0 - 2026-09-04”A second @Id composes the key instead of replacing the first, so an entity relying on that gains a column
and a two-column PRIMARY KEY; narrowing an inherited key is
unaffected. EntityMeta.id is now ids, a list.
0.24.0 - 2026-08-02
Section titled “0.24.0 - 2026-08-02”The pool runs every operation, so a function that writes can take UniversalQuerier (a querier or the pool).
Only a hand-written QuerierPool has anything to do: insertMany, updateMany, upsertOne, upsertMany
and saveMany are no longer optional.
0.23.0 - 2026-08-01
Section titled “0.23.0 - 2026-08-01”Decorators are the standard TC39 ones now. No experimentalDecorators, no emitDecoratorMetadata, no
reflect-metadata. The codemod does most of it and reports what is left. Then, by hand:
targetmust not beesnext, the one target where TypeScript leaves decorators untransformed.- Your build must transform them. esbuild, SWC, Babel
version: '2023-11', Bun andtscdo. Oxc does not, so Vite 8 needs one of the others. uql.config.tsneedsbunornode --import tsxif it imports entities, since decorators are not erasable syntax.- NestJS: use
defineEntity. Nest’s DI needs parameter decorators, and onetsconfig.jsoncannot mix specs. See NestJS. - Node 24 is the minimum.