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
R extends unknown[] - Array of result types for the querynew BoundQuery(query?, bindings?)Create a new bound query.
Syntaxnew BoundQuery() // Empty query new BoundQuery(boundQuery) // Clone existing new BoundQuery(query, bindings?) // From string and bindings
| Parameter | Type | Description |
|---|---|---|
query optional | string | BoundQuery | Query string or existing BoundQuery to clone. |
bindings optional | Record<string, unknown> | Parameter bindings. |
// Empty query const query = new BoundQuery(); // From string const query = new BoundQuery('SELECT * FROM users'); // With bindings const query = new BoundQuery( 'SELECT * FROM users WHERE age > $age', { age: 18 } ); // Clone existing const clone = new BoundQuery(existingQuery);
queryThe query string with parameter placeholders.
Type: string
const query = new BoundQuery( 'SELECT * FROM users WHERE age > $age', { age: 18 } ); console.log(query.query); // 'SELECT * FROM users WHERE age > $age'
bindingsA copy of the parameter bindings.
Type: Record<string, unknown>
console.log(query.bindings); // { age: 18 }
.append()Append another query or string to this query.
Method Syntaxquery.append(other) query.append(queryString, bindings?) query.append`template ${value}`
| Parameter | Type | Description |
|---|---|---|
other | BoundQuery | string | TemplateStringsArray | Query to append. |
bindings optional | Record<string, unknown> | Bindings for the appended query. |
this - Chainable
Append BoundQueryconst base = new BoundQuery('SELECT * FROM users WHERE 1=1'); const filter = new BoundQuery(' AND age > $age', { age: 18 }); base.append(filter);
Append Stringconst query = new BoundQuery('SELECT * FROM users'); query.append(' WHERE active = $active', { active: true });
Append with Templateconst query = new BoundQuery('SELECT * FROM users'); const status = 'active'; query.append` WHERE status = ${status}`;
.bind()Add or update a parameter binding.
Method Syntaxquery.bind(key, value)
| Parameter | Type | Description |
|---|---|---|
key required | string | Parameter name (without $ prefix). |
value required | unknown | Parameter value. |
this - Chainable
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 Syntaxquery.toString()
string - The query string
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();
// Start with base query const query = new BoundQuery('SELECT * FROM products WHERE 1=1'); // Add conditions dynamically 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();
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; } } // Usage const builder = new QueryBuilder('users'); const query = builder .where('status', 'active') .where('verified', true) .limit(10) .build(); const [users] = await db.query(query).collect();
// Define reusable fragments const activeFilter = new BoundQuery('status = $status', { status: 'active' }); const verifiedFilter = new BoundQuery('verified = $verified', { verified: true }); // Combine them const query = new BoundQuery('SELECT * FROM users WHERE '); query.append(activeFilter); query.append(' AND '); query.append(verifiedFilter); const [users] = await db.query(query).collect();
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; } // Usage const query = paginatedQuery('users', 2, 20, { status: 'active' }); const [users] = await db.query(query).collect();
For most cases, the surql template is easier:
// Good: surql template (recommended) const query = surql`SELECT * FROM users WHERE age > ${age}`; // Also good: BoundQuery (more manual) const query = new BoundQuery( 'SELECT * FROM users WHERE age > $age', { age } );
// Good: Consistent parameter naming const query = new BoundQuery( 'SELECT * FROM users WHERE age > $age AND status = $status', { age: 18, status: 'active' } ); // Avoid: Mismatched names const query = new BoundQuery( 'SELECT * FROM users WHERE age > $minAge', { age: 18 } // Wrong key name );
// Good: Incremental building const query = new BoundQuery('SELECT * FROM users WHERE 1=1'); if (filter) { query.append(' AND status = $status', { status: filter }); } // Avoid: String concatenation let queryStr = 'SELECT * FROM users WHERE 1=1'; if (filter) { queryStr += ` AND status = '${filter}'`; // Unsafe! }