Features / Select
Build recursive CTEs with $withRecursive()
Create a
WITH RECURSIVEquery whose callback can reference the CTE while its result shape is being defined.
A common table expression (CTE) is a named query introduced by WITH. A
recursive CTE can also refer to its own rows, which makes it useful for trees,
graphs, and other data with repeated relationships.
Enable it
import { eq, isNull } from 'drizzle-orm'
import 'drizzle-plus/pg/$withRecursive'
// or 'drizzle-plus/mysql/$withRecursive'
// or 'drizzle-plus/sqlite/$withRecursive'
Define the recursive relation
The callback receives a typed reference to the CTE. Use that reference in the
recursive branch of the query, then pass the CTE to db.with():
const tree = db.$withRecursive('category_tree').as(self =>
db
.select({
id: category.id,
parentId: category.parentId,
})
.from(category)
.where(isNull(category.parentId))
.unionAll(
db
.select({
id: category.id,
parentId: category.parentId,
})
.from(category)
.innerJoin(self, eq(category.parentId, self.id))
)
)
const rows = await db.with(tree).select().from(tree)
The example assumes category has id and parentId columns. The anchor and
recursive branches must return compatible columns, just as they would in
handwritten SQL.
Help TypeScript infer the selection
The recursive self-reference can make the selection difficult to infer. If
TypeScript cannot determine the fields, declare the selection type at the
.as<...>() call and keep the anchor and recursive branches aligned.
$withRecursive() marks the CTE as recursive; the final query still needs
db.with(tree) before it can select from the relation.