Skip to content
UQL

Sorting

$sort orders by any field of the entity, by a JSON path inside one, and by a joined relation’s field. A direction is 'asc' / 1 or 'desc' / -1, and keys order by in the order you write them:

const posts = await pool.findMany(Post, {
$select: { title: true, publishedAt: true },
$sort: { publishedAt: 'desc', title: 'asc' },
});
PostgreSQL
ORDER BY "publishedAt" DESC, "title"

A column is nullable in UQL unless it says nullable: false, and each engine has its own idea of where those nulls belong:

Engine Unqualified asc puts nulls
PostgreSQL, CockroachDB, Neon, PGlite, Bun SQL last
SQLite, libSQL, Turso, D1, MySQL, MariaDB, SQL Server first
MongoDB first (a missing field counts as null)

So the same query answers in a different order depending on where it runs. Say which one you want, and it reads the same everywhere:

const posts = await pool.findMany(Post, {
$sort: { publishedAt: 'descNullsLast' },
});

The four placements are ascNullsFirst, ascNullsLast, descNullsFirst and descNullsLast. They work anywhere a direction does: a field, a JSON path, a joined relation’s field, a populated relation’s own $sort, and an aggregate’s.

Each engine gets there its own way, and the result is identical:

PostgreSQL, CockroachDB, SQLite, libSQL, Turso, D1
ORDER BY "publishedAt" DESC NULLS LAST
MySQL, MariaDB
ORDER BY `publishedAt` IS NULL, `publishedAt` DESC
SQL Server
ORDER BY CASE WHEN [publishedAt] IS NULL THEN 1 ELSE 0 END, [publishedAt] DESC

MongoDB has no placement at all, so the pipeline flags each row and orders by the flag first, then takes the flag back off before you see the document.