DucklingClient API Reference
The DucklingClient class provides a high-performance WebSocket interface to DuckDB.
Constructor
Create a new DucklingClient instance.
const client = new DucklingClient ( config : DuckDBSDKConfig );
Configuration object for the client. See Installation for all options.
Methods
connect()
Manually connect to DuckDB server and authenticate with API key.
This method is optional when autoConnect: true (default). The client automatically connects on first query.
await client . connect (): Promise < void >
Example:
const client = new DucklingClient ({
url: 'ws://localhost:3001/ws' ,
apiKey: process . env . DUCKLING_API_KEY ! ,
autoConnect: false // Disable auto-connect
});
// Manually connect
await client . connect ();
Throws:
Error if connection fails
Error if authentication fails
query()
Execute SQL query and return typed results.
await client . query < T = any >( sql : string , params ?: any []) : Promise < T []>
Optional query parameters for parameterized queries
TypeScript type for result rows (defaults to any)
Returns: Promise<T[]> - Array of result rows
Examples:
Basic Query
Typed Query
Parameterized Query
Aggregate Query
const users = await client . query ( 'SELECT * FROM User LIMIT 10' );
console . log ( users );
Throws:
Error if query execution fails
Error if not connected (when autoConnect: false)
queryBatch()
Execute multiple queries in parallel.
await client . queryBatch < T = any >( queries : string []) : Promise < T [][]>
Array of SQL queries to execute in parallel
TypeScript type for result rows
Returns: Promise<T[][]> - Array of result arrays (one per query)
Example:
const results = await client . queryBatch ([
'SELECT COUNT(*) as count FROM User' ,
'SELECT COUNT(*) as count FROM Product' ,
'SELECT COUNT(*) as count FROM Order'
]);
console . log ( 'User count:' , results [ 0 ][ 0 ]. count );
console . log ( 'Product count:' , results [ 1 ][ 0 ]. count );
console . log ( 'Order count:' , results [ 2 ][ 0 ]. count );
Batch queries execute 3-5x faster than sequential execution. See Examples for benchmarks.
queryPaginated()
Execute paginated query with automatic LIMIT and OFFSET.
await client . queryPaginated < T = QueryRow > (
sql : string ,
options : PaginationOptions
): Promise < PaginatedResult < T >>
Base SQL query (without LIMIT/OFFSET)
options
PaginationOptions
required
Pagination options with limit and offset
Returns: Promise<PaginatedResult<T>> - Paginated result with metadata
Example:
const result = await client . queryPaginated < User >(
'SELECT * FROM User ORDER BY createdAt DESC' ,
{ limit: 20 , offset: 0 }
);
console . log ( `Showing ${ result . data . length } users` );
console . log ( `Has more: ${ result . pagination . hasMore } ` );
console . log ( `Offset: ${ result . pagination . offset } ` );
console . log ( `Limit: ${ result . pagination . limit } ` );
PaginatedResult structure:
interface PaginatedResult < T > {
data : T []; // Result rows
pagination : {
offset : number ; // Current offset
limit : number ; // Requested limit
total ?: number ; // Total rows (if available)
hasMore ?: boolean ; // Whether more rows exist
};
}
queryBatchDetailed()
Execute batch queries with individual success/failure handling.
await client . queryBatchDetailed < T = QueryRow > (
requests : BatchQueryRequest []
): Promise < BatchQueryResult < T > [] >
requests
BatchQueryRequest[]
required
Array of query requests with optional parameters
Returns: Promise<BatchQueryResult<T>[]> - Array of results (success or error for each query)
Example:
const requests : BatchQueryRequest [] = [
{ sql: 'SELECT * FROM User LIMIT 10' },
{ sql: 'SELECT * FROM InvalidTable' }, // Will fail
{ sql: 'SELECT * FROM Order LIMIT 10' }
];
const results = await client . queryBatchDetailed ( requests );
results . forEach (( result , index ) => {
if ( result . success ) {
console . log ( `Query ${ index + 1 } : Success - ${ result . data ?. length } rows in ${ result . duration } ms` );
} else {
console . error ( `Query ${ index + 1 } : Failed - ${ result . error } ` );
}
});
BatchQueryResult structure:
interface BatchQueryResult < T > {
query : string ; // Original query
success : boolean ; // Query success status
data ?: T []; // Results (on success)
error ?: string ; // Error message (on failure)
duration ?: number ; // Execution time in ms
}
ping()
Test connection with ping.
await client . ping (): Promise < boolean >
Returns: Promise<boolean> - true if connection is healthy, false otherwise
Example:
const isAlive = await client . ping ();
if ( isAlive ) {
console . log ( '✓ Connection healthy' );
} else {
console . log ( '✗ Connection unhealthy' );
}
Auto-ping is enabled by default (autoPing: true). Manual ping is useful for health checks.
close()
Close WebSocket connection.
Example:
try {
const users = await client . query ( 'SELECT * FROM User' );
console . log ( users );
} finally {
client . close ();
}
Always call close() when done to free resources. Use in finally blocks to ensure cleanup.
isConnected()
Check if client is connected and authenticated.
client . isConnected (): boolean
Returns: boolean - true if connected and authenticated
Example:
if ( client . isConnected ()) {
await client . query ( 'SELECT * FROM User' );
} else {
console . log ( 'Not connected' );
}
getStats()
Get connection statistics.
client . getStats (): ConnectionStats
Returns: ConnectionStats object with connection information
Example:
const stats = client . getStats ();
console . log ( stats );
// {
// connected: true,
// authenticated: true,
// pendingRequests: 2,
// reconnectAttempts: 0,
// url: 'ws://localhost:3001/ws'
// }
ConnectionStats structure:
interface ConnectionStats {
connected : boolean ; // Currently connected and authenticated
authenticated : boolean ; // Successfully authenticated
pendingRequests : number ; // Requests waiting for response
reconnectAttempts : number ; // Reconnection attempts made
url : string ; // WebSocket server URL
}
Event Handling
The client extends EventEmitter and provides strongly-typed events.
on()
Listen to events.
client . on ( event : string , listener : Function ): this
Available Events:
Emitted when successfully connected to server
Emitted when disconnected from server
Emitted when an error occurs
reconnecting
(attempt: number) => void
Emitted when reconnection attempt starts
message
(response: QueryResponse) => void
Emitted when a message is received
Example:
client . on ( 'connected' , () => {
console . log ( 'Connected to DuckDB server' );
});
client . on ( 'disconnected' , () => {
console . log ( 'Disconnected from server' );
});
client . on ( 'error' , ( error : Error ) => {
console . error ( 'WebSocket error:' , error . message );
});
client . on ( 'reconnecting' , ( attempt : number ) => {
console . log ( `Reconnection attempt ${ attempt } ` );
});
Error Handling
Handle query errors with try-catch:
try {
const result = await client . query ( 'SELECT * FROM NonExistentTable' );
} catch ( error ) {
if ( error instanceof Error ) {
console . error ( 'Query failed:' , error . message );
}
}
Common Error Types:
Connection errors : Server unreachable, authentication failed
Query errors : Invalid SQL, table not found, syntax errors
Timeout errors : Query exceeded timeout (30 seconds)
Use Batch Queries for Multiple Queries
Execute multiple queries in parallel with queryBatch() for 3-5x speedup: // ✓ Good - parallel execution
const results = await client . queryBatch ([
'SELECT COUNT(*) FROM User' ,
'SELECT COUNT(*) FROM Product' ,
'SELECT COUNT(*) FROM Order'
]);
// ✗ Bad - sequential execution
const users = await client . query ( 'SELECT COUNT(*) FROM User' );
const products = await client . query ( 'SELECT COUNT(*) FROM Product' );
const orders = await client . query ( 'SELECT COUNT(*) FROM Order' );
Use Connection Pooling for High Concurrency
Create multiple clients for 10,000+ queries/second: const pool = Array . from ({ length: 5 }, () => new DucklingClient ( config ));
// Round-robin query distribution
let currentIndex = 0 ;
function query ( sql : string ) {
const client = pool [ currentIndex ];
currentIndex = ( currentIndex + 1 ) % pool . length ;
return client . query ( sql );
}
See Connection Pool Example for full implementation.
Enable Auto-Ping for Long-Lived Connections
Auto-ping keeps connections alive automatically (enabled by default): const client = new DucklingClient ({
url: 'ws://localhost:3001/ws' ,
apiKey: process . env . DUCKLING_API_KEY ! ,
autoPing: true , // Default: true
pingInterval: 30000 // Default: 30 seconds
});
Use TypeScript Types for Type Safety
Define schema types for full IntelliSense and compile-time checks: interface User {
id : number ;
name : string ;
email : string ;
}
const users = await client . query < User >( 'SELECT * FROM User' );
// TypeScript knows: users[0].email exists
Next Steps
TypeScript Types Learn about type definitions
Examples See real-world usage examples