Skip to content

Streaming large result sets ​

queryStream yields rows lazily in adaptively-sized batches — the next op_fetch only fires as you consume, so a huge table never lands in memory at once and an early break stops after just a batch or two:

ts
for await (const row of db.queryStream<Order>('select * from big_table order by id')) {
  process(row);            // backpressure-friendly; break any time
}

// Node stream ergonomics:
import { Readable } from 'node:stream';
const stream = Readable.from(db.queryStream('select * from big_table'));

db.queryStream runs in its own transaction (committed at end, rolled back on error/break); tx.queryStream streams within a transaction you own.

Concurrency

Don't run other statements on the same connection mid-stream — use another connection or the pool for concurrency.

Streaming with blobs ​

Combine queryStream with lazy blobs and blobReadAhead to export large blob tables at full wire utilization — see Blobs → Performance.

ts
await db.transaction(async (tx) => {
  for await (const row of tx.queryStream('select id, doc from files', [], {
    blobs: 'lazy',
    blobReadAhead: 2,
  })) {
    await (row.DOC as Blob).toFile(`out/${row.ID}.bin`);
  }
});

MIT Licensed. Contains data derived from the Firebird project under IDPL/IPL.