Interface StorageAdapter

Core SQL storage adapter interface.

Every adapter implementation must fulfill this contract to ensure compatibility across different SQL backends. Check the capabilities property to determine which optional features are supported.

All methods should throw meaningful errors when operations fail. Adapters should not silently fail or return undefined on errors.

Adapters should document their thread-safety guarantees. SQLite adapters typically don't support concurrent writes, while PostgreSQL does.

interface StorageAdapter {
    kind: string;
    capabilities: ReadonlySet<StorageCapability>;
    open(options?: StorageOpenOptions): Promise<void>;
    run(statement: string, parameters?: StorageParameters): Promise<StorageRunResult>;
    get<T>(statement: string, parameters?: StorageParameters): Promise<null | T>;
    all<T>(statement: string, parameters?: StorageParameters): Promise<T[]>;
    exec(script: string): Promise<void>;
    transaction<T>(fn: ((trx: StorageAdapter) => Promise<T>)): Promise<T>;
    close(): Promise<void>;
    batch?(operations: BatchOperation[]): Promise<BatchResult>;
    prepare?<T>(statement: string): PreparedStatement<T>;
}

Implemented by

Properties

kind: string

Identifier for logging/diagnostics (e.g., 'better-sqlite3', 'postgres').

capabilities: ReadonlySet<StorageCapability>

Capability flags indicating supported features.

Methods

  • Opens the underlying connection or initializes the backing store.

    Parameters

    Returns Promise<void>

    If connection cannot be established

    await adapter.open({ filePath: '/path/to/db.sqlite3' });
    
  • Executes a mutation statement (INSERT, UPDATE, DELETE).

    Parameters

    • statement: string

      SQL statement to execute

    • Optionalparameters: StorageParameters

      Optional parameters for the statement

    Returns Promise<StorageRunResult>

    Metadata about affected rows

    If statement execution fails

    const result = await adapter.run('INSERT INTO users (name) VALUES (?)', ['John']);
    console.log(`Inserted with ID: ${result.lastInsertRowid}`);
  • Retrieves a single row (or null if none found).

    Type Parameters

    • T = unknown

    Parameters

    • statement: string

      SQL SELECT statement

    • Optionalparameters: StorageParameters

      Optional parameters for the statement

    Returns Promise<null | T>

    First row or null if no results

    If query execution fails

    const user = await adapter.get<User>('SELECT * FROM users WHERE id = ?', [123]);
    
  • Retrieves all rows returned by the statement.

    WARNING: For large result sets, this loads everything into memory. Consider using streaming (if supported) for large queries.

    Type Parameters

    • T = unknown

    Parameters

    • statement: string

      SQL SELECT statement

    • Optionalparameters: StorageParameters

      Optional parameters for the statement

    Returns Promise<T[]>

    Array of all matching rows (empty array if none)

    If query execution fails

    const users = await adapter.all<User>('SELECT * FROM users WHERE age > ?', [18]);
    
  • Executes a script containing multiple SQL statements.

    Statements are typically delimited by semicolons. This is useful for running migrations or initialization scripts. No results are returned.

    Parameters

    • script: string

      SQL script with multiple statements

    Returns Promise<void>

    If any statement in the script fails

    await adapter.exec(`
    CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);
    CREATE INDEX idx_users_name ON users(name);
    `);
  • Executes a callback within a database transaction.

    The transaction is automatically committed on success or rolled back on error. Nested transactions may not be supported by all adapters.

    Type Parameters

    • T

    Parameters

    Returns Promise<T>

    Result of the callback function

    Transaction is rolled back and error is re-thrown

    const result = await adapter.transaction(async (trx) => {
    await trx.run('INSERT INTO accounts (balance) VALUES (?)', [100]);
    await trx.run('INSERT INTO logs (action) VALUES (?)', ['account_created']);
    return { success: true };
    });
  • Closes the underlying connection and releases resources.

    After calling close(), the adapter should not be used again. Always close adapters when done to prevent resource leaks.

    Returns Promise<void>

    try {
    await adapter.open();
    // ... use adapter ...
    } finally {
    await adapter.close();
    }
  • Executes multiple operations in a batch (if supported).

    This is more efficient than executing operations individually, especially for bulk inserts. Check if 'batch' capability is present.

    Parameters

    Returns Promise<BatchResult>

    Batch execution results

    If adapter doesn't support batch operations

    if (adapter.capabilities.has('batch')) {
    const result = await adapter.batch([
    { statement: 'INSERT INTO users (name) VALUES (?)', parameters: ['Alice'] },
    { statement: 'INSERT INTO users (name) VALUES (?)', parameters: ['Bob'] }
    ]);
    }
  • Returns a prepared statement for repeated execution (if supported).

    Prepared statements improve performance for frequently executed queries and provide better protection against SQL injection. Check if 'prepared' capability is present.

    Type Parameters

    • T = unknown

    Parameters

    • statement: string

      SQL statement to prepare

    Returns PreparedStatement<T>

    Prepared statement handle

    If adapter doesn't support prepared statements