From 4fc5c2dea18c1fb0251502d397ad7c927b1e3ed0 Mon Sep 17 00:00:00 2001 From: Leka74 Date: Tue, 26 Aug 2025 12:29:57 +0200 Subject: [PATCH 1/2] feat: expose raw sqlite client in `sql-sqlite-bun` and `sql-sqlite-node` exposes a `rawClient` property to both of the SqliteClient interfaces to provide access to the underlying database client --- packages/sql-sqlite-bun/examples/Client.test.ts | 6 ++++++ packages/sql-sqlite-bun/src/SqliteClient.ts | 10 ++++++++++ packages/sql-sqlite-node/src/SqliteClient.ts | 10 ++++++++++ packages/sql-sqlite-node/test/Client.test.ts | 10 ++++++++++ 4 files changed, 36 insertions(+) diff --git a/packages/sql-sqlite-bun/examples/Client.test.ts b/packages/sql-sqlite-bun/examples/Client.test.ts index 9ece40664bf..b01aec33443 100644 --- a/packages/sql-sqlite-bun/examples/Client.test.ts +++ b/packages/sql-sqlite-bun/examples/Client.test.ts @@ -17,6 +17,7 @@ describe("Client", () => { test("works", () => Effect.gen(function*() { const sql = yield* makeClient + const rawClient = yield* sql.rawClient yield* sql`CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)` yield* sql`INSERT INTO test (name) VALUES ('hello')` let rows = yield* sql`SELECT * FROM test` @@ -27,5 +28,10 @@ describe("Client", () => { { id: 1, name: "hello" }, { id: 2, name: "world" } ]) + const rawRows = yield* Effect.try(() => rawClient.query("SELECT * FROM test").all()) + expect(rawRows).toEqual([ + { id: 1, name: "hello" }, + { id: 2, name: "world" } + ]) }).pipe(Effect.scoped, Effect.runPromise)) }) diff --git a/packages/sql-sqlite-bun/src/SqliteClient.ts b/packages/sql-sqlite-bun/src/SqliteClient.ts index 937d8e1dd75..ddbc8016166 100644 --- a/packages/sql-sqlite-bun/src/SqliteClient.ts +++ b/packages/sql-sqlite-bun/src/SqliteClient.ts @@ -15,6 +15,12 @@ import { identity } from "effect/Function" import * as Layer from "effect/Layer" import * as Scope from "effect/Scope" +/** + * @category models + * @since 1.0.0 + */ +export type { Database } + const ATTR_DB_SYSTEM_NAME = "db.system.name" /** @@ -36,6 +42,7 @@ export type TypeId = typeof TypeId export interface SqliteClient extends Client.SqlClient { readonly [TypeId]: TypeId readonly config: SqliteClientConfig + readonly rawClient: Effect.Effect readonly export: Effect.Effect readonly loadExtension: (path: string) => Effect.Effect @@ -67,6 +74,7 @@ export interface SqliteClientConfig { } interface SqliteConnection extends Connection { + readonly database: Database readonly export: Effect.Effect readonly loadExtension: (path: string) => Effect.Effect } @@ -117,6 +125,7 @@ export const make = ( }) return identity({ + database: db, execute(sql, params, transformRows) { return transformRows ? Effect.map(run(sql, params), transformRows) @@ -177,6 +186,7 @@ export const make = ( { [TypeId]: TypeId as TypeId, config: options, + rawClient: Effect.flatMap(acquirer, (_) => Effect.succeed(_.database)), export: Effect.flatMap(acquirer, (_) => _.export), loadExtension: (path: string) => Effect.flatMap(acquirer, (_) => _.loadExtension(path)) } diff --git a/packages/sql-sqlite-node/src/SqliteClient.ts b/packages/sql-sqlite-node/src/SqliteClient.ts index b4b0bc734a6..64e05a703b5 100644 --- a/packages/sql-sqlite-node/src/SqliteClient.ts +++ b/packages/sql-sqlite-node/src/SqliteClient.ts @@ -17,6 +17,12 @@ import { identity } from "effect/Function" import * as Layer from "effect/Layer" import * as Scope from "effect/Scope" +/** + * @category models + * @since 1.0.0 + */ +export type Database = Sqlite.Database + const ATTR_DB_SYSTEM_NAME = "db.system.name" /** @@ -38,6 +44,7 @@ export type TypeId = typeof TypeId export interface SqliteClient extends Client.SqlClient { readonly [TypeId]: TypeId readonly config: SqliteClientConfig + readonly rawClient: Effect.Effect readonly export: Effect.Effect readonly backup: (destination: string) => Effect.Effect readonly loadExtension: (path: string) => Effect.Effect @@ -78,6 +85,7 @@ export interface SqliteClientConfig { } interface SqliteConnection extends Connection { + readonly database: Database readonly export: Effect.Effect readonly backup: (destination: string) => Effect.Effect readonly loadExtension: (path: string) => Effect.Effect @@ -173,6 +181,7 @@ export const make = ( ) return identity({ + database: db, execute(sql, params, transformRows) { return transformRows ? Effect.map(run(sql, params), transformRows) @@ -241,6 +250,7 @@ export const make = ( { [TypeId]: TypeId as TypeId, config: options, + rawClient: Effect.flatMap(acquirer, (_) => Effect.succeed(_.database)), export: Effect.flatMap(acquirer, (_) => _.export), backup: (destination: string) => Effect.flatMap(acquirer, (_) => _.backup(destination)), loadExtension: (path: string) => Effect.flatMap(acquirer, (_) => _.loadExtension(path)) diff --git a/packages/sql-sqlite-node/test/Client.test.ts b/packages/sql-sqlite-node/test/Client.test.ts index 7f0acf330ea..7e98e37c6f0 100644 --- a/packages/sql-sqlite-node/test/Client.test.ts +++ b/packages/sql-sqlite-node/test/Client.test.ts @@ -52,6 +52,16 @@ describe("Client", () => { ]) })) + it.scoped("should work with rawClient", () => + Effect.gen(function*() { + const sql = yield* makeClient + const rawClient = yield* sql.rawClient + yield* Effect.sync(() => rawClient.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)")) + yield* Effect.sync(() => rawClient.exec("INSERT INTO test (name) VALUES ('hello')")) + const rows = yield* Effect.try(() => rawClient.prepare("SELECT * FROM test").all()) + assert.deepStrictEqual(rows, [{ id: 1, name: "hello" }]) + })) + it.scoped("withTransaction", () => Effect.gen(function*() { const sql = yield* makeClient From 190cb3a5f8bd8257ea258c7e8471f9bac75159d1 Mon Sep 17 00:00:00 2001 From: Leka74 Date: Tue, 26 Aug 2025 12:48:59 +0200 Subject: [PATCH 2/2] add changeset --- .changeset/crazy-icons-rush.md | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .changeset/crazy-icons-rush.md diff --git a/.changeset/crazy-icons-rush.md b/.changeset/crazy-icons-rush.md new file mode 100644 index 00000000000..b48cfb4307c --- /dev/null +++ b/.changeset/crazy-icons-rush.md @@ -0,0 +1,36 @@ +--- +"@effect/sql-sqlite-node": minor +"@effect/sql-sqlite-bun": minor +--- + +## Raw Database Client Access + +Both `@effect/sql-sqlite-node` and `@effect/sql-sqlite-bun` now expose the underlying database client through a new `rawClient` property. This provides direct access to the native database APIs when needed for advanced operations. + +**Example** + +```ts +import { SqliteClient } from "@effect/sql-sqlite-bun" +import { Effect } from "effect" + +const program = Effect.gen(function* () { + const client = yield* SqliteClient.make({ filename: "test.db" }) + + // Access the raw Bun SQLite Database instance + const rawDb = yield* client.rawClient + + // Use native database methods directly + const result = yield* Effect.try(() => + rawClient.prepare("SELECT * FROM users").all() + ) + + return result +}) +``` + +The `rawClient` property returns: + +- `Effect` for `@effect/sql-sqlite-bun` (Bun's native SQLite Database) +- `Effect` for `@effect/sql-sqlite-node` (better-sqlite3 Database) + +This enables users to access vendor-specific functionality while maintaining the Effect-based workflow.