SurrealDB
SurrealDB Docs Logo

Enter a search query

Navigation

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() // Empty query new BoundQuery(boundQuery) // Clone existing new BoundQuery(query, bindings?) // From string and bindings

Parameters

ParameterTypeDescription
query optionalstring | BoundQueryQuery string or existing BoundQuery to clone.
bindings optionalRecord<string, unknown>Parameter bindings.

Examples

// 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);

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); // 'SELECT * FROM users WHERE age > $age'

bindings

A copy of the parameter bindings.

Type: Record<string, unknown>

console.log(query.bindings);
// { age: 18 }

Methods

.append()

Append another query or string to this query.

Method Syntax
query.append(other) query.append(queryString, bindings?) query.append`template ${value}`

Parameters

ParameterTypeDescription
otherBoundQuery | string | TemplateStringsArrayQuery to append.
bindings optionalRecord<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

ParameterTypeDescription
key requiredstringParameter name (without $ prefix).
value requiredunknownParameter 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

// 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();

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; } } // 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();

Reusable Query Fragments

// 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();

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();

Pagination Helper

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();

Best Practices

1. Use surql Template Instead

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 } );

2. Validate Parameter Names

// 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 );

3. Use append() for Dynamic Queries

// 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! }

See Also