BoundQuery<R>
The BoundQuery class represents a parameterized SurrealQL query with bound variables, providing safe query composition and preventing SQL injection.
Import:
import { BoundQuery } from 'surrealdb';
Source: utils/bound-query.ts
Type Parameters
R extends unknown[] - Array of result types for the query
Constructor
new BoundQuery(query?, bindings?)
Create a new bound query.
Syntax
new BoundQuery()
new BoundQuery(boundQuery)
new BoundQuery(query, bindings?)
Parameters
| Parameter | Type | Description |
|---|
query optional | string | BoundQuery | Query string or existing BoundQuery to clone. |
bindings optional | Record<string, unknown> | Parameter bindings. |
Examples
const query = new BoundQuery();
const query = new BoundQuery('SELECT * FROM users');
const query = new BoundQuery(
'SELECT * FROM users WHERE age > $age',
{ age: 18 }
);
const clone = new BoundQuery(existingQuery);
Properties
query
The query string with parameter placeholders.
Type: string
const query = new BoundQuery(
'SELECT * FROM users WHERE age > $age',
{ age: 18 }
);
console.log(query.query);
bindings
A copy of the parameter bindings.
Type: Record<string, unknown>
console.log(query.bindings);
Methods
.append()
Append another query or string to this query.
Method Syntax
query.append(other)
query.append(queryString, bindings?)
query.append`template ${value}`
Parameters
| Parameter | Type | Description |
|---|
other | BoundQuery | string | TemplateStringsArray | Query to append. |
bindings optional | Record<string, unknown> | Bindings for the appended query. |
Returns
this - Chainable
Examples
Append BoundQuery
const base = new BoundQuery('SELECT * FROM users WHERE 1=1');
const filter = new BoundQuery(' AND age > $age', { age: 18 });
base.append(filter);
Append String
const query = new BoundQuery('SELECT * FROM users');
query.append(' WHERE active = $active', { active: true });
Append with Template
const query = new BoundQuery('SELECT * FROM users');
const status = 'active';
query.append` WHERE status = ${status}`;
.bind()
Add or update a parameter binding.
Method Syntax
query.bind(key, value)
Parameters
| Parameter | Type | Description |
|---|
key required | string | Parameter name (without $ prefix). |
value required | unknown | Parameter value. |
Returns
this - Chainable
Example
const query = new BoundQuery('SELECT * FROM users WHERE age > $age');
query.bind('age', 18);
query.bind('status', 'active');
const [users] = await db.query(query).collect();
.toString()
Get the query string.
Method Syntax
query.toString()
Returns
string - The query string
Complete Examples
Basic Parameterized Query
import { BoundQuery } from 'surrealdb';
const query = new BoundQuery(
'SELECT * FROM users WHERE age >= $minAge AND status = $status',
{
minAge: 18,
status: 'active'
}
);
const [users] = await db.query(query).collect();
Building Queries Incrementally
const query = new BoundQuery('SELECT * FROM products WHERE 1=1');
if (category) {
query.append(' AND category = $category', { category });
}
if (minPrice) {
query.append(' AND price >= $minPrice', { minPrice });
}
if (maxPrice) {
query.append(' AND price <= $maxPrice', { maxPrice });
}
query.append(' ORDER BY created_at DESC LIMIT $limit', { limit: 10 });
const [products] = await db.query(query).collect();
Query Builder Pattern
class QueryBuilder {
private query: BoundQuery;
constructor(table: string) {
this.query = new BoundQuery(`SELECT * FROM ${table} WHERE 1=1`);
}
where(field: string, value: unknown): this {
this.query.append(` AND ${field} = $${field}`, { [field]: value });
return this;
}
limit(count: number): this {
this.query.append(' LIMIT $limit', { limit: count });
return this;
}
build(): BoundQuery {
return this.query;
}
}
const builder = new QueryBuilder('users');
const query = builder
.where('status', 'active')
.where('verified', true)
.limit(10)
.build();
const [users] = await db.query(query).collect();
Reusable Query Fragments
const activeFilter = new BoundQuery('status = $status', { status: 'active' });
const verifiedFilter = new BoundQuery('verified = $verified', { verified: true });
const query = new BoundQuery('SELECT * FROM users WHERE ');
query.append(activeFilter);
query.append(' AND ');
query.append(verifiedFilter);
const [users] = await db.query(query).collect();
Complex Multi-Statement Query
const userId = new RecordId('users', 'john');
const postData = {
title: 'My Post',
content: 'Content here'
};
const query = new BoundQuery();
query.append('BEGIN TRANSACTION;');
query.append(
'UPDATE $userId SET post_count += 1;',
{ userId }
);
query.append(
'CREATE posts SET author = $author, title = $title, content = $content;',
{
author: userId,
title: postData.title,
content: postData.content
}
);
query.append('COMMIT TRANSACTION;');
await db.query(query).collect();
function paginatedQuery(
table: string,
page: number,
pageSize: number,
filters?: Record<string, unknown>
): BoundQuery {
const query = new BoundQuery(`SELECT * FROM ${table} WHERE 1=1`);
if (filters) {
for (const [key, value] of Object.entries(filters)) {
query.append(` AND ${key} = $${key}`, { [key]: value });
}
}
const offset = (page - 1) * pageSize;
query.append(' START $offset LIMIT $limit', {
offset,
limit: pageSize
});
return query;
}
const query = paginatedQuery('users', 2, 20, { status: 'active' });
const [users] = await db.query(query).collect();
Best Practices
1. Use surql Template Instead
For most cases, the surql template is easier:
const query = surql`SELECT * FROM users WHERE age > ${age}`;
const query = new BoundQuery(
'SELECT * FROM users WHERE age > $age',
{ age }
);
2. Validate Parameter Names
const query = new BoundQuery(
'SELECT * FROM users WHERE age > $age AND status = $status',
{ age: 18, status: 'active' }
);
const query = new BoundQuery(
'SELECT * FROM users WHERE age > $minAge',
{ age: 18 }
);
3. Use append() for Dynamic Queries
const query = new BoundQuery('SELECT * FROM users WHERE 1=1');
if (filter) {
query.append(' AND status = $status', { status: filter });
}
let queryStr = 'SELECT * FROM users WHERE 1=1';
if (filter) {
queryStr += ` AND status = '${filter}'`;
}
See Also