Skip to content

Cursor Streaming

For large result sets that exceed available memory, UQL provides findManyStream(). Instead of loading the entire result set into a TypeScript array, it returns an AsyncIterable that allows you to process rows one-by-one as they arrive from the database.

The findManyStream method accepts the same shape of query object as findMany, but not every findMany feature is available on the stream path (see Relations & streaming below).

const results = querier.findManyStream(User, {
$select: { id: true, email: true },
$where: { status: 'active' },
});
for await (const user of results) {
// Process each user row-by-row
console.log(`Processing: ${user.email}`);
}

UQL keeps streaming memory-friendly by not running the same follow-up work as findMany for every backend.

BackendJoinable relations (e.g. many-to-one, one-to-one)To-many (one-to-many, many-to-many)
SQL (AbstractSqlQuerier)Still emitted in the streamed SQL (joins + projected columns).Not supported — To-many relations are filled as a post-processing step which is incompatible with row-by-row streaming. Requesting these keys in $select or $populate throws a TypeError.
MongoDB (MongodbQuerier)Not supported — MongoDB streams use a plain find cursor which cannot efficiently load UQL’s aggregation-based relations. Requesting any relation keys in $select or $populate throws a TypeError.Same as joinable.

For relation-heavy reads, use findMany with $populate.

  1. Memory Efficiency: You can process 1,000,000 rows with the same memory footprint as processing 10 rows.
  2. Early Processing: Start handling the first row before the database has even finished finding the last one.
  3. Backpressure: UQL respects the database cursor’s speed, ensuring your application isn’t overwhelmed by data.

UQL uses the optimal streaming mechanism for each individual driver:

DriverImplementation
PostgreSQL (pg)Native cursor via pg-query-stream.
MySQL (mysql2)Result set streaming via .stream().
SQLite (better-sqlite3)Iteration via .iterate().
Bun SQL (bun:sql)Native iteration via generator-wrapped iterate().
MongoDB (mongodb)Native MongoDB Cursor.
LibSQL / D1Emulated streaming (async row fetching).

Like all UQL queries, the stream query is fully serializable. If you are using the Fullstack Bridge, the streaming protocol is handled automatically over the network, allowing you to stream data from your database directly to a browser or mobile app.


Senior Insight: Streaming is powerful but holds a database connection open for the duration of the loop. Always ensure your processing logic inside the for await loop is fast, or use a technical queue if you need to perform heavy work on each row.