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.

101 lines (82 loc) 3.03 kB
import { describe, expect, it } from "@effect/vitest"; import { Array, Effect, Option } from "effect"; import * as Database from "../src/Sqlite.js"; import { Generated, Table } from "../src/Schema.js"; import * as Schema from "effect/Schema"; import * as kysely from "kysely"; import BetterSqlite3 from "better-sqlite3"; describe("database", () => { class Users extends Table({ id: Generated(Schema.Int), name: Schema.String, }) { } const TestSchema = Schema.Struct({ users: Users, }) type TestSchema = typeof TestSchema.Encoded class TestDatabase extends Database.make<TestSchema, TestDatabase>( "TestDatabase" ) { } const acquire = Effect.promise(async () => { const db = new kysely.Kysely<TestSchema>({ dialect: new kysely.SqliteDialect({ database: new BetterSqlite3(":memory:"), }), }); await db.schema .createTable("users") .addColumn("id", "integer", (column) => column.primaryKey().autoIncrement() ) .addColumn("name", "text") .execute(); return db; }); const createUser = TestDatabase.schema.single({ Request: Users.select.fields.name, Result: Users.select, execute: (db, name) => db.insertInto("users").values({ name }).returningAll(), }); const findUser = TestDatabase.schema.findOne({ Request: Users.select.fields.id, Result: Users.select, execute: (db, id) => db.selectFrom("users").where("id", "=", id).selectAll(), }); it.scoped("allows making SQL queries", () => Effect.gen(function* () { const created = yield* createUser("Test"); const selected = yield* findUser(created.id); expect(Option.some(created)).toEqual(selected); }).pipe(Effect.provide(TestDatabase.layer({ acquire }))) ); it.scoped("allows making SQL queries with transactions", () => Effect.gen(function* () { const created = yield* createUser("Test"); const selected = yield* findUser(created.id); expect(Option.some(created)).toEqual(selected); }).pipe( TestDatabase.withTransaction, Effect.provide(TestDatabase.layer({ acquire })), ) ); it.scoped("allows resolving by id", () => Effect.gen(function* () { const total = 10 const created = yield* Effect.all(globalThis.Array.from({ length: total }, (_, i) => createUser(`Test ${i}`))) const resolver = yield* TestDatabase.resolver.findById('FindUser', { Id: Users.select.fields.id, Result: Users.select, ResultId: (user) => user.id, execute: (db, ids) => { expect(ids.length).toBe(total); return db.selectFrom("users").where("id", 'in', ids).selectAll(); }, }) const selected = yield* Effect.all(created.map(user => resolver.execute(user.id)), { batching: true }) const zipped = Array.zip(created, selected) for (const [created, selected] of zipped) { expect(Option.some(created)).toEqual(selected); } }).pipe(Effect.provide(TestDatabase.layer({ acquire })))) });