postgres: added pg and postgres, and examples for xata and neon

This commit is contained in:
dswbx
2025-06-06 16:52:07 +02:00
parent abbd372ddf
commit 58c7aba1a4
21 changed files with 509 additions and 247 deletions

View File

@@ -0,0 +1,32 @@
import { Kysely, PostgresDialect } from "kysely";
import { PostgresIntrospector } from "./PostgresIntrospector";
import { PostgresConnection, plugins } from "./PostgresConnection";
import { customIntrospector } from "bknd/data";
import $pg from "pg";
export type PgPostgresConnectionConfig = $pg.PoolConfig;
export class PgPostgresConnection extends PostgresConnection {
private pool: $pg.Pool;
constructor(config: PgPostgresConnectionConfig) {
const pool = new $pg.Pool(config);
const kysely = new Kysely({
dialect: customIntrospector(PostgresDialect, PostgresIntrospector, {
excludeTables: [],
}).create({ pool }),
plugins,
});
super(kysely);
this.pool = pool;
}
override async close(): Promise<void> {
await this.pool.end();
}
}
export function pg(config: PgPostgresConnectionConfig): PgPostgresConnection {
return new PgPostgresConnection(config);
}

View File

@@ -1,56 +1,33 @@
import { Connection, type FieldSpec, type SchemaResponse } from "bknd/data";
import { Connection, type DbFunctions, type FieldSpec, type SchemaResponse } from "bknd/data";
import {
ParseJSONResultsPlugin,
type ColumnDataType,
type ColumnDefinitionBuilder,
type DatabaseIntrospector,
Kysely,
ParseJSONResultsPlugin,
PostgresDialect,
type Kysely,
type KyselyPlugin,
type SelectQueryBuilder,
} from "kysely";
import { jsonArrayFrom, jsonBuildObject, jsonObjectFrom } from "kysely/helpers/postgres";
import pg from "pg";
import { PostgresIntrospector } from "./PostgresIntrospector";
export type PostgresConnectionConfig = pg.PoolConfig;
export type QB = SelectQueryBuilder<any, any, any>;
const plugins = [new ParseJSONResultsPlugin()];
export const plugins = [new ParseJSONResultsPlugin()];
class CustomPostgresDialect extends PostgresDialect {
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
return new PostgresIntrospector(db, {
excludeTables: [],
});
}
}
export class PostgresConnection extends Connection {
export abstract class PostgresConnection<DB = any> extends Connection<DB> {
protected override readonly supported = {
batching: true,
};
private pool: pg.Pool;
constructor(config: PostgresConnectionConfig) {
const pool = new pg.Pool(config);
const kysely = new Kysely({
dialect: new CustomPostgresDialect({
pool,
}),
plugins,
//log: ["query", "error"],
});
constructor(kysely: Kysely<DB>, fn?: Partial<DbFunctions>, _plugins?: KyselyPlugin[]) {
super(
kysely,
{
fn ?? {
jsonArrayFrom,
jsonBuildObject,
jsonObjectFrom,
},
plugins,
_plugins ?? plugins,
);
this.pool = pool;
}
override getFieldSchema(spec: FieldSpec): SchemaResponse {
@@ -90,10 +67,6 @@ export class PostgresConnection extends Connection {
];
}
override async close(): Promise<void> {
await this.pool.end();
}
protected override async batch<Queries extends QB[]>(
queries: [...Queries],
): Promise<{

View File

@@ -0,0 +1,41 @@
import { Kysely } from "kysely";
import { PostgresIntrospector } from "./PostgresIntrospector";
import { PostgresConnection, plugins } from "./PostgresConnection";
import { customIntrospector } from "bknd/data";
import { PostgresJSDialect } from "kysely-postgres-js";
import $postgresJs, { type Sql, type Options, type PostgresType } from "postgres";
export type PostgresJsConfig = Options<Record<string, PostgresType>>;
export class PostgresJsConnection extends PostgresConnection {
private postgres: Sql;
constructor(opts: { postgres: Sql }) {
const kysely = new Kysely({
dialect: customIntrospector(PostgresJSDialect, PostgresIntrospector, {
excludeTables: [],
}).create({ postgres: opts.postgres }),
plugins,
});
super(kysely);
this.postgres = opts.postgres;
}
override async close(): Promise<void> {
await this.postgres.end();
}
}
export function postgresJs(
connectionString: string,
config?: PostgresJsConfig,
): PostgresJsConnection;
export function postgresJs(config: PostgresJsConfig): PostgresJsConnection;
export function postgresJs(
first: PostgresJsConfig | string,
second?: PostgresJsConfig,
): PostgresJsConnection {
const postgres = typeof first === "string" ? $postgresJs(first, second) : $postgresJs(first);
return new PostgresJsConnection({ postgres });
}

View File

@@ -0,0 +1,43 @@
import type { Constructor } from "bknd/core";
import { customIntrospector, type DbFunctions } from "bknd/data";
import { Kysely, type Dialect, type KyselyPlugin } from "kysely";
import { plugins, PostgresConnection } from "./PostgresConnection";
import { PostgresIntrospector } from "./PostgresIntrospector";
export type CustomPostgresConnection = {
supports?: PostgresConnection["supported"];
fn?: Partial<DbFunctions>;
plugins?: KyselyPlugin[];
excludeTables?: string[];
};
export function createCustomPostgresConnection<
T extends Constructor<Dialect>,
C extends ConstructorParameters<T>[0],
>(
dialect: Constructor<Dialect>,
options?: CustomPostgresConnection,
): (config: C) => PostgresConnection<any> {
const supported = {
batching: true,
...((options?.supports ?? {}) as any),
};
return (config: C) =>
new (class extends PostgresConnection<any> {
protected override readonly supported = supported;
constructor(config: C) {
super(
new Kysely({
dialect: customIntrospector(dialect, PostgresIntrospector, {
excludeTables: options?.excludeTables ?? [],
}).create(config),
plugins: options?.plugins ?? plugins,
}),
options?.fn,
options?.plugins,
);
}
})(config);
}

View File

@@ -1,2 +1,5 @@
export { PostgresConnection, type PostgresConnectionConfig } from "./PostgresConnection";
export { pg, PgPostgresConnection, type PgPostgresConnectionConfig } from "./PgPostgresConnection";
export { PostgresIntrospector } from "./PostgresIntrospector";
export { PostgresConnection, type QB, plugins } from "./PostgresConnection";
export { postgresJs, PostgresJsConnection, type PostgresJsConfig } from "./PostgresJsConnection";
export { createCustomPostgresConnection } from "./custom";