Backend withQueryReader Code Examples
This page documents the use of withQueryReader — a synchronous, backend-only method available on both IModelDb and ECDb.
withQueryReader is the synchronous counterpart of IModelDb.createQueryReader. Instead of returning an async ECSqlReader, it prepares the statement, invokes a callback with an ECSqlSyncReader.
Unlike createQueryReader, which pages results through the async concurrent-query infrastructure, withQueryReader performs true row-by-row stepping directly against the underlying prepared statement. Each call to step() executes exactly one step — no rows are buffered, prefetched, or cached internally. This makes withQueryReader well-suited for scenarios where synchronous execution and live data fetching are important.
Note:
withQueryReaderis only available on the backend. For frontend usage, use IModelConnection.createQueryReader.
See also:
- General ECSQL Code Examples —
createQueryReaderusage that applies to both frontend and backend - Backend ECSQL Code Examples —
withPreparedStatementusage - Executing ECSQL in the Backend
- ECSQL Row Formats
The withQueryReader Function
Here is the TypeScript method signature for withQueryReader:
- The
ecsqlstring is the ECSQL statement to execute. - The
callbackreceives an ECSqlSyncReader scoped to the call. Do not keep a reference to the reader outside the callback — attempting tostepit after the callback returns will throw an error. - The
paramsargument of type QueryBinder contains any bindings for the ECSQL statement. - The
configargument of typeSynchronousQueryOptionscontrols how results are formatted. The available options are a subset of QueryOptions:rowFormat— how result rows are structured. See ECSQL Row Formats.abbreviateBlobs— whentrue, binary values are abbreviated rather than fully serialized.convertClassIdsToClassNames— whentrue, ECClassId values are returned as fully-qualified class names.
Iterating Over Query Results
There are three primary ways to consume results from withQueryReader:
1. Synchronous Iterator (for...of)
Use ECSqlSyncReader as a synchronous iterator with a for...of loop:
Each iterated value is a QueryRowProxy. See Handling a Row of Query Results below.
2. Manual Stepping with step()
Step through rows one at a time using ECSqlSyncReader.step:
step() returns true if there are rows left to be stepped through, and false when all rows have been consumed.
3. Capture All Results with toArray()
Collect all remaining rows at once into an array:
Each element of the returned array is a JavaScript literal whose shape is determined by the rowFormat option. See ECSQL Row Formats for details.
Handling a Row of Query Results
When using the iterator or step(), each row is a QueryRowProxy. Values can be accessed by index or by name.
Accessing Row Values By Index
Default behavior — column values are ordered by their position in the SELECT clause:
The row format does not affect index-based access; only the order of columns in the SELECT statement matters.
Accessing Row Values By Name
Use ECSQL property names as keys:
Converting a Row to a JavaScript Literal
Call .toRow() on the QueryRowProxy to convert it to a plain JavaScript object. The structure of the object depends on the rowFormat in config:
Row Formats
The row format is controlled by rowFormat in the config parameter and mirrors the behavior of QueryOptions used with createQueryReader. See ECSQL Row Formats for full details.
QueryRowFormat.UseECSqlPropertyIndexes (default)
Values are accessed by the zero-based index of the column in the SELECT clause. Rows returned from toArray() are plain arrays:
QueryRowFormat.UseECSqlPropertyNames
Values are keyed by their ECSQL property names. Rows from toArray() are objects:
QueryRowFormat.UseJsPropertyNames
Values are keyed by JavaScript-style property names (e.g., ECInstanceId → id, ECClassId → className):
Parameter Bindings
Parameters are supplied as a QueryBinder instance. See ECSQL Parameter Types for type information.
Positional Parameters
Named Parameters
Navigation Properties
Navigation properties require a NavigationBindingValue:
Id Set Parameters
Bind a set of Id64String values for use with InVirtualSet:
Getting Column Metadata
Use ECSqlSyncReader.getMetaData to retrieve metadata about the result columns. This is available before or after calling step():
Returning a Value from the Callback
withQueryReader returns whatever value the callback returns, making it easy to compute and return an aggregated result:
Important Constraints
Do not use the reader outside the callback. The underlying statement is disposed when the callback returns. Calling
step()on a reader that has escaped its callback will throw an error:// ❌ This will throw when step() is called const escaped = iModel.withQueryReader("SELECT * FROM BisCore.Element", (reader) => reader); escaped.step(); // throws: "Statement is not prepared"Do not call
clearCaches()or close the database during iteration. Either action invalidates the prepared statement and causes a subsequentstep()call to throw.SynchronousQueryOptionsis a restricted subset ofQueryOptions. Options that only apply to the async concurrent-query infrastructure — such aslimit,priority,restartToken,delay,usePrimaryConn, andquota— are not available inwithQueryReader.
Last Updated: 06 March, 2026