Rerun a query with new values
Integrator guide
When a user asks the same question with a new value, such as another month, rebind the query AskDB already generated instead of calling the model again. AskDB checks the stored template again and takes the tenant IDs from the scope you pass now.
Before you start
Section titled “Before you start”- A service that calls
ask(), through@askdb/clientor@askdb/core. Embed in a Node app sets one up. parameterizeleft on, which is the default.ask()then asks the model for a template of the query as well, at the cost of some extra output tokens.
1. Keep the prepared query
Section titled “1. Keep the prepared query”ask() returns a preparedQuery when the query has at least one value to change, such as a date or a name, and the model’s template matches its SQL. It holds the template and the definition of each parameter, but no values. It’s plain JSON, so you can store it with the question:
// askdb initialized at startup with createAskDb() — see Embed in a Node app.const result = await askdb.ask("How many orders shipped in March?", { tenantScope });
if (result.preparedQuery) { await savedQueries.save({ question, preparedQuery: result.preparedQuery });}Without a preparedQuery, call ask() again for the new value. It’s also absent with a custom AskDialect.
2. Bind the new values
Section titled “2. Bind the new values”Pass the stored preparedQuery, the new values and the caller’s tenant scope:
const rebound = await askdb.bind(preparedQuery, { month: "2026-04" }, { tenantScope });Key each value by a parameter’s name. Pass one for each entry in preparedQuery.parameters whose source is "question", of its type. The entries with source: "tenant" are the tenant placeholders: AskDB fills them from the scope.
askdb.bind() makes no model call. It checks the template again, the way ask() checks the model’s SQL:
- The SQL checks run again on the stored template, under the schema, tenant scope and tenant policy you pass now. A template that bound yesterday can be refused today, for example after you tighten the policy.
- Tenant IDs come only from
tenantScope. A value keyedtenant_*throws. A query first generated for one tenant gets the IDs of the scope you pass now, so you can rebind it for another caller. Authorizing that scope stays your job, as forask(). - A tenant check that fails throws, also under a policy whose
enforcementiswarn, unless you passacceptWarnings: ["tenant"].
If you call @askdb/core directly, bindPreparedQuery(preparedQuery, values, { schema, dialect, tenantScope }) does the same. If ask() ran with your own DialectSpec, pass the same spec as dialect to either function. See client.bind() and bindPreparedQuery() for every option.
3. Run the rebound SQL
Section titled “3. Run the rebound SQL”Run rebound.sql with rebound.tenantParams, the tenant IDs for its markers, the way you run ask()’s result. With the runCapped from Cap the rows a query returns:
const { rows, truncated } = await runCapped(rebound.sql, rebound.tenantParams);rebound.unboundSql with rebound.params is the other pair, with every value as a parameter. Run one pair or the other: never mix their arrays.
If it fails
Section titled “If it fails”| Error | Fix |
|---|---|
QueryParameterError MISSING_VALUE: Missing value for parameter '<name>'. | Pass a value for each parameter whose source is "question". |
QueryParameterError INVALID_VALUE | The value doesn’t fit the parameter’s type or cardinality. Convert it first, for example a string from a form to a number. |
QueryParameterError TENANT_VALUE_SUPPLIED | Remove the tenant_* key from the values, and pass the scope as tenantScope. |
QueryParameterError DIALECT_SPEC_REQUIRED | ask() ran with your own DialectSpec, or the template was stored before 1.0.0-beta.46. Pass the spec ask() ran with as dialect, or the built-in id, such as "postgres", if it ran with the built-in rules. |
TenantScopeError MISSING_SCOPE | Pass the caller’s tenantScope. |
TenantGuardrailError | The template fails the tenant check under this scope or the current policy. Under enforcement: warn, pass acceptWarnings: ["tenant"] if you accept the warning. Otherwise call ask() again for this caller. |
QueryParameterError reasons lists the rest.
Read next
Section titled “Read next”© 2026 Yahya Gilany

