Skip to main content

Generic

You can integrate any SQLite or Postgres database driver by adapting it to the ElectricSQL DatabaseAdapter interface:

export interface DatabaseAdapter {
// Database connection instance from your driver library.
db: AnyDatabase

// Run the provided sql statement. A statement has the
// form of `{sql: string, bindParams?: string[]}`.
run(statement: Statement): Promise<RunResult>

// Run an array of sql statements within a transaction.
runInTransaction(...statements: Statement[]): Promise<RunResult>

// Executes the function in isolation from any other queries/transactions executed through this adapter.
// Useful to execute queries that cannot be executed inside a transaction but still guarantee isolation from other queries.
runExclusively<T>(
f: (adapter: UncoordinatedDatabaseAdapter) => Promise<T> | T
): Promise<T>

// Run a query statement and return the results as an
// array of rows.
query(statement: Statement): Promise<Row[]>

// Run the provided function inside a transaction.
transaction<T>(
f: (tx: Transaction, setResult: (res: T) => void) => void
): Promise<T | void>

// Get the tables potentially used by an SQL statement.
// This supports reactivity for raw SQL use via the
// `db.liveRawQuery` function.
tableNames(statement: Statement): QualifiedTablename[]
}

For convenience, we provide two generic database adapters, SerialDatabaseAdapter and BatchDatabaseAdapter. These implement the parts of the interface that are common to most adapters. This allows you to implement your own driver adapters using a simpler interface.

export abstract class SerialDatabaseAdapter implements DatabaseAdapter {
// Run a single SQL statement against the DB
abstract _run(stmt: Statement): Promise<RunResult>
// Run a single SQL query against the DB
abstract _query(stmt: Statement): Promise<Row[]>
}

// Use `BatchDatabaseAdapter` if the underlying driver
// supports batch execution of SQL statements
export abstract class BatchDatabaseAdapter implements DatabaseAdapter {
// Run a single SQL statement against the DB
abstract _run(stmt: Statement): Promise<RunResult>
// Run a single SQL query against the DB
abstract _query(stmt: Statement): Promise<Row[]>
// Run several SQL statements againt the DB in a single batch
abstract execBatch(statements: Statement[]): Promise<RunResult>
}

The best guidance for this is to look at the existing driver implementations. You can then build on the base electrify function to implement your own electrify function, e.g.:

export const electrify = async <T, DB extends DbSchema<any>>(
db: T,
dbDescription: DB,
config: ElectricConfig,
opts?: ElectrifyOptions
): Promise<ElectricClient<DB>> => {
const dbName = db.name
const adapter = opts?.adapter || new MyDatabaseAdapter(db)
const socketFactory = opts?.socketFactory || new WebSocketWebFactory()

const client = await baseElectrify(
dbName,
dbDescription,
adapter,
socketFactory,
config,
opts
)

return client
}

For more help / pointers, let us know on Discord and we'll be happy to help you with the integration.