UNPKG

effect-sql-kysely

Version:

A full-featured integration between `@effect/sql` and `Kysely` that provides type-safe database operations with Effect's powerful error handling and resource management.

79 lines (78 loc) 3.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.makeSqlClient = makeSqlClient; exports.makeKyselyEffect = makeKyselyEffect; const sql_1 = require("@effect/sql"); const effect_1 = require("effect"); const Cause_1 = require("effect/Cause"); const kysely_1 = require("kysely"); const beginConnection_js_1 = require("./internal/beginConnection.js"); const transformRows = sql_1.Statement.defaultTransforms((s) => s, false).array; /** * Low-level ability to construct an @effect/sql SqlClient interface for a Kysely database. */ function makeSqlClient({ database, compiler, spanAttributes = [], chunkSize = 16, }) { class ConnectionImpl { db; constructor(db) { this.db = db; } executeUnprepared(sql, params) { return effect_1.Effect.tryPromise({ try: () => this.db.executeQuery(compileSqlQuery(sql, params)).then((r) => transformRows(r.rows)), catch: (cause) => new sql_1.SqlError.SqlError({ cause }), }); } execute(sql, params) { return effect_1.Effect.tryPromise({ try: () => this.db.executeQuery(compileSqlQuery(sql, params)).then((r) => transformRows(r.rows)), catch: (cause) => new sql_1.SqlError.SqlError({ cause }), }); } executeWithoutTransform(sql, params) { return effect_1.Effect.tryPromise({ try: () => this.db.executeQuery(compileSqlQuery(sql, params)).then((r) => r.rows), catch: (cause) => new sql_1.SqlError.SqlError({ cause }), }); } executeValues(sql, params) { return effect_1.Effect.map(this.executeRaw(sql, params), (results) => results.map((x) => Object.values(x))); } executeRaw(sql, params) { return effect_1.Effect.tryPromise({ try: () => this.db.executeQuery(compileSqlQuery(sql, params)).then((r) => transformRows(r.rows)), catch: (cause) => new sql_1.SqlError.SqlError({ cause }), }); } executeStream(sql, params) { const query = compileSqlQuery(sql, params); return effect_1.Stream.suspend(() => effect_1.Stream.mapChunks(effect_1.Stream.fromAsyncIterable(this.db.getExecutor().stream(query, chunkSize), (cause) => new sql_1.SqlError.SqlError({ cause })), effect_1.Chunk.flatMap((result) => effect_1.Chunk.unsafeFromArray(result.rows)))); } } return sql_1.SqlClient.make({ // Our default connection is managed by Kysely acquirer: effect_1.Effect.succeed(new ConnectionImpl(database)), // Our SQL statement compiler compiler, // We don't utilize db.transaction() because Sql.client.make will handle the actual transaction // But we do ensure that all queries are run within a single connection transactionAcquirer: effect_1.Effect.map(effect_1.Effect.acquireRelease(effect_1.Effect.promise(() => (0, beginConnection_js_1.beginConnection)(database)), (conn, exit) => effect_1.Effect.promise(() => effect_1.Exit.match(exit, { // If the scope fails we rollback the transaction onFailure: (cause) => conn.fail((0, Cause_1.squash)(cause)), // If the scope succeeds we commit the transaction onSuccess: () => conn.success(), }))), ({ conn }) => new ConnectionImpl(conn)), spanAttributes, }); } function makeKyselyEffect(database, sql) { return (f) => { // We utilize compile() and sql.unsafe to enable utilizing Effect's notion of a Transaction const compiled = f(database).compile(); // eslint-disable-next-line @typescript-eslint/no-explicit-any return sql.unsafe(compiled.sql, compiled.parameters); }; } function compileSqlQuery(sql, params) { return kysely_1.CompiledQuery.raw(sql, params); }