@probitas/client-sql-duckdb

DuckDB client for Probitas scenario testing framework.

This package provides a DuckDB client designed for integration testing, with analytical query capabilities and Parquet/CSV file support.

Features

  • Query Execution: Parameterized queries with type-safe results
  • Transactions: Full transaction support
  • File Formats: Native support for Parquet, CSV, and JSON files
  • In-Memory Databases: Perfect for isolated test scenarios
  • Analytical Queries: Optimized for OLAP workloads
  • Resource Management: Implements AsyncDisposable for proper cleanup

Installation

deno add jsr:@probitas/client-sql-duckdb

Quick Start

import { createDuckDbClient } from "@probitas/client-sql-duckdb";

// In-memory database for testing
const client = await createDuckDbClient({
  path: ":memory:",
});

// Query from Parquet files
const result = await client.query<{ id: number; name: string }>(
  "SELECT id, name FROM read_parquet('data/*.parquet') WHERE active = ?",
  [true]
);
console.log(result.rows);

// Analytical queries
const stats = await client.query(`
  SELECT
    date_trunc('month', created_at) as month,
    COUNT(*) as count,
    AVG(amount) as avg_amount
  FROM transactions
  GROUP BY 1
  ORDER BY 1
`);

await client.close();

Transactions

await client.transaction(async (tx) => {
  await tx.query("INSERT INTO accounts (id, balance) VALUES (?, ?)", [1, 100]);
  await tx.query("INSERT INTO accounts (id, balance) VALUES (?, ?)", [2, 200]);
  // Automatically committed if no error, rolled back on exception
});

Using with using Statement

await using client = await createDuckDbClient({ path: ":memory:" });

const result = await client.query("SELECT 42 as answer");
// Client automatically closed when block exits
Package Description
@probitas/client-sql Common SQL types and utilities
@probitas/client-sql-postgres PostgreSQL client
@probitas/client-sql-mysql MySQL client
@probitas/client-sql-sqlite SQLite client

Installation

deno add jsr:@probitas/client-sql-duckdb

Classes

class

#CatalogError

class CatalogError extends DuckDbError

Error thrown for catalog errors (table not found, etc.).

NameDescription
name
duckdbKind
Constructor
new CatalogError(message: string, options?: DuckDbErrorOptions)
Properties
  • readonlynamestring
  • readonlyduckdbKind"catalog"
class

#ConstraintError

class ConstraintError extends SqlError
ExtendsSqlError

Error thrown when a constraint violation occurs.

NameDescription
name
kind
constraint
Constructor
new ConstraintError(message: string, constraint: string, options?: SqlErrorOptions)
Properties
  • readonlynamestring
  • readonlykind"constraint"
  • readonlyconstraintstring
class

#DeadlockError

class DeadlockError extends SqlError
ExtendsSqlError

Error thrown when a deadlock is detected.

NameDescription
name
kind
Constructor
new DeadlockError(message: string, options?: SqlErrorOptions)
Properties
  • readonlynamestring
  • readonlykind"deadlock"
class

#DuckDbError

class DuckDbError extends SqlError
ExtendsSqlError

Base error class for DuckDB-specific errors. Extends SqlError with DuckDB-specific properties.

NameDescription
name
errorType
Constructor
new DuckDbError(message: string, options?: DuckDbErrorOptions)
Properties
  • readonlynamestring
  • readonlyerrorType?string
class

#DuckDbTransactionImpl

class DuckDbTransactionImpl implements SqlTransaction
ImplementsSqlTransaction
NameDescription
begin()Begin a new transaction.
query()
queryOne()
commit()
rollback()
Constructor
new DuckDbTransactionImpl(conn: DuckDBConnection)
Methods
static begin(): unknown

Begin a new transaction.

query(): unknown
queryOne(): unknown
commit(): unknown
rollback(): unknown
class

#IoError

class IoError extends DuckDbError

Error thrown for IO-related errors (file not found, permission denied, etc.).

NameDescription
name
duckdbKind
Constructor
new IoError(message: string, options?: DuckDbErrorOptions)
Properties
  • readonlynamestring
  • readonlyduckdbKind"io"
class

#QuerySyntaxError

class QuerySyntaxError extends SqlError
ExtendsSqlError

Error thrown when a SQL query has syntax errors.

NameDescription
name
kind
Constructor
new QuerySyntaxError(message: string, options?: SqlErrorOptions)
Properties
  • readonlynamestring
  • readonlykind"query"
class

#SqlError

class SqlError extends ClientError

Base error class for SQL-specific errors. Extends ClientError with SQL-specific properties.

NameDescription
name
kind
sqlState
Constructor
new SqlError(message: string, kind: SqlErrorKind, options?: SqlErrorOptions)
Properties
  • readonlynamestring
  • readonlykindSqlErrorKind
  • readonlysqlState?string
class

#SqlQueryResult

class SqlQueryResult<T = Record<string, any>>

SQL query result with rows, metadata, and transformation methods.

NameDescription
ok
rows
rowCount
duration
metadata
map()Map rows to a new type.
as()Create class instances from rows.
Constructor
new SqlQueryResult(init: SqlQueryResultInit<T>)
Properties
Methods
map(): unknown

Map rows to a new type.

as(): unknown

Create class instances from rows.

class

#SqlRows

class SqlRows<T> extends Array<T>
ExtendsArray<T>

Row array with first/last helper methods. Implements ReadonlyArray by extending Array.

NameDescription
first()Get the first row, or undefined if empty.
firstOrThrow()Get the first row, or throw if empty.
last()Get the last row, or undefined if empty.
lastOrThrow()Get the last row, or throw if empty.
Constructor
new SqlRows(items: readonly T[])
Methods
first(): unknown

Get the first row, or undefined if empty.

firstOrThrow(): unknown

Get the first row, or throw if empty.

last(): unknown

Get the last row, or undefined if empty.

lastOrThrow(): unknown

Get the last row, or throw if empty.

Interfaces

interface

#DuckDbClient

interface DuckDbClient extends AsyncDisposable

DuckDB client interface.

NameDescription
configThe client configuration.
dialectThe SQL dialect identifier.
query()Execute a SQL query.
queryOne()Execute a query and return the first row or undefined.
transaction()Execute a function within a transaction.
queryParquet()Query a Parquet file directly.
queryCsv()Query a CSV file directly.
close()Close the database connection.
Properties
  • readonlyconfigDuckDbClientConfig

    The client configuration.

  • readonlydialect"duckdb"

    The SQL dialect identifier.

Methods
query<T = Record<string, any>>(sql: string, params?: unknown[]): Promise<SqlQueryResult<T>>

Execute a SQL query.

Parameters
  • sqlstring
    • SQL query string
  • params?unknown[]
    • Optional query parameters
queryOne<T = Record<string, any>>(sql: string, params?: unknown[]): Promise<T | undefined>

Execute a query and return the first row or undefined.

Parameters
  • sqlstring
    • SQL query string
  • params?unknown[]
    • Optional query parameters
transaction<T>(fn: (tx: SqlTransaction) => unknown, options?: SqlTransactionOptions | DuckDbTransactionOptions): Promise<T>

Execute a function within a transaction. Automatically commits on success or rolls back on error.

Parameters
queryParquet<T = Record<string, any>>(path: string): Promise<SqlQueryResult<T>>

Query a Parquet file directly. DuckDB can read Parquet files without importing them.

Parameters
  • pathstring
    • Path to the Parquet file
queryCsv<T = Record<string, any>>(path: string): Promise<SqlQueryResult<T>>

Query a CSV file directly. DuckDB can read CSV files without importing them.

Parameters
  • pathstring
    • Path to the CSV file
close(): Promise<void>

Close the database connection.

interface

#DuckDbClientConfig

interface DuckDbClientConfig extends CommonOptions

Configuration for creating a DuckDB client.

NameDescription
pathDatabase file path.
readonlyOpen the database in read-only mode.
Properties
  • readonlypath?string

    Database file path. Use :memory: or omit for an in-memory database.

  • readonlyreadonly?boolean

    Open the database in read-only mode.

interface

#DuckDbErrorOptions

interface DuckDbErrorOptions extends SqlErrorOptions

Options for DuckDbError constructor.

NameDescription
errorTypeDuckDB error type if available.
Properties
  • readonlyerrorType?string

    DuckDB error type if available.

interface

#DuckDbTransactionOptions

interface DuckDbTransactionOptions extends SqlTransactionOptions

DuckDB-specific transaction options.

interface

#SqlErrorOptions

interface SqlErrorOptions extends ErrorOptions

Options for SqlError constructor.

NameDescription
sqlStateSQL State code (e.g., "23505" for unique violation)
Properties
  • readonlysqlState?string

    SQL State code (e.g., "23505" for unique violation)

interface

#SqlQueryResultExpectation

interface SqlQueryResultExpectation<T>

Expectation interface for SQL query results. All methods return this for chaining.

NameDescription
ok()Verify query succeeded
notOk()Verify query failed
noContent()Verify result has no rows
hasContent()Verify result has rows
rows()Verify exact row count
rowsAtLeast()Verify minimum row count
rowsAtMost()Verify maximum row count
rowCount()Verify exact affected row count
rowCountAtLeast()Verify minimum affected row count
rowCountAtMost()Verify maximum affected row count
rowContains()Verify a row contains the given subset
rowMatch()Custom row validation
mapContains()Verify mapped data contains the given subset
mapMatch()Custom mapped data validation
asContains()Verify instance contains the given subset
asMatch()Custom instance validation
lastInsertId()Verify exact lastInsertId
hasLastInsertId()Verify lastInsertId is present
durationLessThan()Verify query duration is below threshold
Methods
ok(): this

Verify query succeeded

notOk(): this

Verify query failed

noContent(): this

Verify result has no rows

hasContent(): this

Verify result has rows

rows(count: number): this

Verify exact row count

Parameters
  • countnumber
rowsAtLeast(count: number): this

Verify minimum row count

Parameters
  • countnumber
rowsAtMost(count: number): this

Verify maximum row count

Parameters
  • countnumber
rowCount(count: number): this

Verify exact affected row count

Parameters
  • countnumber
rowCountAtLeast(count: number): this

Verify minimum affected row count

Parameters
  • countnumber
rowCountAtMost(count: number): this

Verify maximum affected row count

Parameters
  • countnumber
rowContains(subset: Partial<T>): this

Verify a row contains the given subset

Parameters
  • subsetPartial<T>
rowMatch(matcher: (rows: SqlRows<T>) => unknown): this

Custom row validation

Parameters
  • matcher(rows: SqlRows<T>) => unknown
mapContains<U>(mapper: (row: T) => unknown, subset: Partial<U>): this

Verify mapped data contains the given subset

Parameters
  • mapper(row: T) => unknown
  • subsetPartial<U>
mapMatch<U>(mapper: (row: T) => unknown, matcher: (mapped: U[]) => unknown): this

Custom mapped data validation

Parameters
  • mapper(row: T) => unknown
  • matcher(mapped: U[]) => unknown
asContains<U>(ctor: (row: T) => unknown, subset: Partial<U>): this

Verify instance contains the given subset

Parameters
  • ctor(row: T) => unknown
  • subsetPartial<U>
asMatch<U>(ctor: (row: T) => unknown, matcher: (instances: U[]) => unknown): this

Custom instance validation

Parameters
  • ctor(row: T) => unknown
  • matcher(instances: U[]) => unknown
lastInsertId(expected: bigint | string): this

Verify exact lastInsertId

Parameters
  • expectedbigint | string
hasLastInsertId(): this

Verify lastInsertId is present

durationLessThan(ms: number): this

Verify query duration is below threshold

Parameters
  • msnumber
interface

#SqlQueryResultInit

interface SqlQueryResultInit<T>

Options for creating a SqlQueryResult.

NameDescription
okWhether the query succeeded
rowsThe result rows
rowCountNumber of affected rows (for INSERT/UPDATE/DELETE)
durationQuery execution duration in milliseconds
metadataAdditional metadata
Properties
  • readonlyokboolean

    Whether the query succeeded

  • readonlyrowsreadonly T[]

    The result rows

  • readonlyrowCountnumber

    Number of affected rows (for INSERT/UPDATE/DELETE)

  • readonlydurationnumber

    Query execution duration in milliseconds

  • readonlymetadataSqlQueryResultMetadata

    Additional metadata

interface

#SqlQueryResultMetadata

interface SqlQueryResultMetadata

Metadata for SQL query results.

NameDescription
lastInsertIdLast inserted ID (for INSERT statements)
warningsWarning messages from the database
Properties
  • readonlylastInsertId?bigint | string

    Last inserted ID (for INSERT statements)

  • readonlywarnings?readonly string[]

    Warning messages from the database

interface

#SqlTransaction

interface SqlTransaction

SQL transaction interface. Implementations should provide actual database-specific transaction handling.

NameDescription
query()Execute a query within the transaction.
queryOne()Execute a query and return the first row or undefined.
commit()Commit the transaction.
rollback()Rollback the transaction.
Methods
query<T = Record<string, any>>(sql: string, params?: unknown[]): Promise<SqlQueryResult<T>>

Execute a query within the transaction.

Parameters
  • sqlstring
    • SQL query string
  • params?unknown[]
    • Optional query parameters
queryOne<T = Record<string, any>>(sql: string, params?: unknown[]): Promise<T | undefined>

Execute a query and return the first row or undefined.

Parameters
  • sqlstring
    • SQL query string
  • params?unknown[]
    • Optional query parameters
commit(): Promise<void>

Commit the transaction.

rollback(): Promise<void>

Rollback the transaction.

interface

#SqlTransactionOptions

interface SqlTransactionOptions

Options for starting a transaction.

NameDescription
isolationLevelIsolation level for the transaction
Properties

Functions

function

#convertDuckDbError

function convertDuckDbError(error: unknown): SqlError

Convert a DuckDB error to the appropriate error class.

DuckDB errors are classified based on message content analysis.

Parameters
  • errorunknown
function

#createDuckDbClient

async function createDuckDbClient(config: DuckDbClientConfig): Promise<DuckDbClient>

Create a new DuckDB client instance.

The client provides parameterized queries, transaction support, and DuckDB-specific features like direct Parquet and CSV file querying.

Parameters
Returns

Promise<DuckDbClient> — A promise resolving to a new DuckDB client instance

Examples

Using in-memory database (default)

const client = await createDuckDbClient({});

const result = await client.query("SELECT 42 as answer");
console.log(result.rows.first());  // { answer: 42 }

await client.close();

Using file-based database

const client = await createDuckDbClient({
  path: "./data.duckdb",
});

Query Parquet files directly

// No need to import - query directly from Parquet
const result = await client.queryParquet<{ id: number; value: string }>(
  "./data/events.parquet"
);

Query CSV files directly

const result = await client.queryCsv<{ name: string; age: number }>(
  "./data/users.csv"
);

Transaction with auto-commit/rollback

await client.transaction(async (tx) => {
  await tx.query("INSERT INTO users VALUES ($1, $2)", [1, "Alice"]);
  await tx.query("INSERT INTO users VALUES ($1, $2)", [2, "Bob"]);
});

Using await using for automatic cleanup

await using client = await createDuckDbClient({});

const result = await client.query("SELECT 1");
// Client automatically closed when scope exits
function

#expectSqlQueryResult

function expectSqlQueryResult<T = Record<string, any>>(result: SqlQueryResult<T>): SqlQueryResultExpectation<T>

Create an expectation for a SQL query result.

Parameters

Type Aliases

type

#DuckDbErrorKind

type DuckDbErrorKind = "io" | "catalog" | "parser" | "binder"

DuckDB-specific error kinds.

type

#SqlErrorKind

type SqlErrorKind = "query" | "constraint" | "deadlock" | "unknown"

SQL-specific error kinds.

type

#SqlIsolationLevel

type SqlIsolationLevel = "read_uncommitted" | "read_committed" | "repeatable_read" | "serializable"

Transaction isolation level.

Search Documentation