alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
116 lines (99 loc) • 3.36 kB
text/typescript
import { AlephaError } from "alepha";
import { sql } from "drizzle-orm";
import type { BunSQLDatabase } from "drizzle-orm/bun-sql";
import type { PgDatabase } from "drizzle-orm/pg-core";
import { PostgresProvider } from "./PostgresProvider.ts";
/**
* Bun PostgreSQL provider using Drizzle ORM with Bun's native SQL client.
*
* This provider uses Bun's built-in SQL class for PostgreSQL connections,
* which provides excellent performance on the Bun runtime.
*
* @example
* ```ts
* // Set DATABASE_URL environment variable
* // DATABASE_URL=postgres://user:password@localhost:5432/database
*
* // Or configure programmatically
* alepha.with({
* provide: DatabaseProvider,
* use: BunPostgresProvider,
* });
* ```
*/
export class BunPostgresProvider extends PostgresProvider {
protected client?: Bun.SQL;
protected bunDb?: BunSQLDatabase;
/**
* Get the Drizzle Postgres database instance.
*/
public override get db(): PgDatabase<any> {
if (!this.bunDb) {
throw new AlephaError("Database not initialized");
}
return this.bunDb as unknown as PgDatabase<any>;
}
protected override async executeMigrations(
migrationsFolder: string,
): Promise<void> {
if (this.schema !== "public") {
await this.db.execute(
sql.raw(`SET search_path TO ${this.schema}, public`),
);
}
const { migrate } = await import("drizzle-orm/bun-sql/migrator");
await migrate(this.bunDb!, {
migrationsFolder,
migrationsTable: this.migrationsTable,
});
}
// -------------------------------------------------------------------------------------------------------------------
public async connect(): Promise<void> {
this.log.debug("Connect ..");
// Check if we're running in Bun
if (typeof Bun === "undefined") {
throw new AlephaError(
"BunPostgresProvider requires the Bun runtime. Use NodePostgresProvider for Node.js.",
);
}
const { drizzle } = await import("drizzle-orm/bun-sql");
// Create Bun SQL client with pool options
// Set search_path via connection URL so all pool connections use the correct schema
let connectionUrl = this.url;
if (this.schema !== "public") {
const separator = connectionUrl.includes("?") ? "&" : "?";
connectionUrl += `${separator}search_path=${this.schema},public`;
}
const bunOptions: Record<string, any> = { url: connectionUrl };
if (this.pgEnv.POOL_MAX != null) {
bunOptions.max = this.pgEnv.POOL_MAX;
}
if (this.pgEnv.POOL_IDLE_TIMEOUT != null) {
bunOptions.idleTimeout = this.pgEnv.POOL_IDLE_TIMEOUT;
}
if (this.pgEnv.POOL_CONNECT_TIMEOUT != null) {
bunOptions.connectionTimeout = this.pgEnv.POOL_CONNECT_TIMEOUT;
}
this.client = new Bun.SQL(bunOptions);
// Test connection
await this.client.unsafe("SELECT 1");
this.bunDb = drizzle({
client: this.client,
logger: {
logQuery: (query: string, params: unknown[]) => {
this.log.trace(query, { params });
},
},
});
this.log.info("Connection OK");
}
public async close(): Promise<void> {
if (this.client) {
this.log.debug("Close...");
await this.client.close();
this.client = undefined;
this.bunDb = undefined;
this.log.info("Connection closed");
}
}
}