mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-17 04:46:05 +00:00
Merge pull request #182 from bknd-io/feat/postgres-improvements
postgres: added `pg` and `postgres`, and examples for xata and neon
This commit is contained in:
@@ -23,6 +23,12 @@ function hasColors() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const __consoles = {
|
const __consoles = {
|
||||||
|
critical: {
|
||||||
|
prefix: "CRT",
|
||||||
|
color: colors.red,
|
||||||
|
args_color: colors.red,
|
||||||
|
original: console.error,
|
||||||
|
},
|
||||||
error: {
|
error: {
|
||||||
prefix: "ERR",
|
prefix: "ERR",
|
||||||
color: colors.red,
|
color: colors.red,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export function disableConsoleLog(severities: ConsoleSeverity[] = ["log", "warn"
|
|||||||
severities.forEach((severity) => {
|
severities.forEach((severity) => {
|
||||||
console[severity] = () => null;
|
console[severity] = () => null;
|
||||||
});
|
});
|
||||||
$console.setLevel("error");
|
$console.setLevel("critical");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function enableConsoleLog() {
|
export function enableConsoleLog() {
|
||||||
|
|||||||
@@ -344,7 +344,6 @@ export class DataController extends Controller {
|
|||||||
if (!this.entityExists(entity)) {
|
if (!this.entityExists(entity)) {
|
||||||
return this.notFound(c);
|
return this.notFound(c);
|
||||||
}
|
}
|
||||||
console.log("id", id);
|
|
||||||
const options = c.req.valid("query") as RepoQuery;
|
const options = c.req.valid("query") as RepoQuery;
|
||||||
const result = await this.em.repository(entity).findId(id, options);
|
const result = await this.em.repository(entity).findId(id, options);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import {
|
|||||||
type AliasableExpression,
|
type AliasableExpression,
|
||||||
type ColumnBuilderCallback,
|
type ColumnBuilderCallback,
|
||||||
type ColumnDataType,
|
type ColumnDataType,
|
||||||
|
type DatabaseIntrospector,
|
||||||
|
type Dialect,
|
||||||
type Expression,
|
type Expression,
|
||||||
type Kysely,
|
type Kysely,
|
||||||
type KyselyPlugin,
|
type KyselyPlugin,
|
||||||
@@ -12,7 +14,8 @@ import {
|
|||||||
type Simplify,
|
type Simplify,
|
||||||
sql,
|
sql,
|
||||||
} from "kysely";
|
} from "kysely";
|
||||||
import type { BaseIntrospector } from "./BaseIntrospector";
|
import type { BaseIntrospector, BaseIntrospectorConfig } from "./BaseIntrospector";
|
||||||
|
import type { Constructor } from "core";
|
||||||
|
|
||||||
export type QB = SelectQueryBuilder<any, any, any>;
|
export type QB = SelectQueryBuilder<any, any, any>;
|
||||||
|
|
||||||
@@ -159,3 +162,19 @@ export abstract class Connection<DB = any> {
|
|||||||
// no-op by default
|
// no-op by default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function customIntrospector<T extends Constructor<Dialect>>(
|
||||||
|
dialect: T,
|
||||||
|
introspector: Constructor<DatabaseIntrospector>,
|
||||||
|
options: BaseIntrospectorConfig = {},
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
create(...args: ConstructorParameters<T>) {
|
||||||
|
return new (class extends dialect {
|
||||||
|
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
||||||
|
return new introspector(db, options);
|
||||||
|
}
|
||||||
|
})(...args);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export {
|
|||||||
type IndexSpec,
|
type IndexSpec,
|
||||||
type DbFunctions,
|
type DbFunctions,
|
||||||
type SchemaResponse,
|
type SchemaResponse,
|
||||||
|
customIntrospector,
|
||||||
} from "./Connection";
|
} from "./Connection";
|
||||||
|
|
||||||
// sqlite
|
// sqlite
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ export class ModuleManager {
|
|||||||
const context = this.ctx(true);
|
const context = this.ctx(true);
|
||||||
|
|
||||||
for (const key in MODULES) {
|
for (const key in MODULES) {
|
||||||
const moduleConfig = key in initial ? initial[key] : {};
|
const moduleConfig = initial && key in initial ? initial[key] : {};
|
||||||
const module = new MODULES[key](moduleConfig, context) as Module;
|
const module = new MODULES[key](moduleConfig, context) as Module;
|
||||||
module.setListener(async (c) => {
|
module.setListener(async (c) => {
|
||||||
await this.onModuleConfigUpdated(key, c);
|
await this.onModuleConfigUpdated(key, c);
|
||||||
|
|||||||
@@ -391,7 +391,7 @@ function EntityField({
|
|||||||
allowDeselect={false}
|
allowDeselect={false}
|
||||||
control={control}
|
control={control}
|
||||||
size="xs"
|
size="xs"
|
||||||
className="w-20"
|
className="w-22"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
67
bun.lock
67
bun.lock
@@ -26,7 +26,7 @@
|
|||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"name": "bknd",
|
"name": "bknd",
|
||||||
"version": "0.13.1-rc.0",
|
"version": "0.14.0-rc.0",
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cfworker/json-schema": "^4.1.1",
|
"@cfworker/json-schema": "^4.1.1",
|
||||||
@@ -45,6 +45,7 @@
|
|||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"fast-xml-parser": "^5.0.8",
|
"fast-xml-parser": "^5.0.8",
|
||||||
|
"hono": "^4.7.11",
|
||||||
"json-schema-form-react": "^0.0.2",
|
"json-schema-form-react": "^0.0.2",
|
||||||
"json-schema-library": "10.0.0-rc7",
|
"json-schema-library": "10.0.0-rc7",
|
||||||
"json-schema-to-ts": "^3.1.1",
|
"json-schema-to-ts": "^3.1.1",
|
||||||
@@ -82,7 +83,6 @@
|
|||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"hono": "4.7.11",
|
|
||||||
"jotai": "^2.12.2",
|
"jotai": "^2.12.2",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^26.0.0",
|
||||||
"jsonv-ts": "^0.1.0",
|
"jsonv-ts": "^0.1.0",
|
||||||
@@ -149,19 +149,24 @@
|
|||||||
},
|
},
|
||||||
"packages/postgres": {
|
"packages/postgres": {
|
||||||
"name": "@bknd/postgres",
|
"name": "@bknd/postgres",
|
||||||
"version": "0.0.1",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
|
||||||
"kysely": "^0.27.6",
|
|
||||||
"pg": "^8.14.0",
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.2.5",
|
"@types/bun": "^1.2.5",
|
||||||
"@types/node": "^22.13.10",
|
"@types/node": "^22.13.10",
|
||||||
"@types/pg": "^8.11.11",
|
"@types/pg": "^8.11.11",
|
||||||
|
"@xata.io/client": "^0.0.0-next.v93343b9646f57a1e5c51c35eccf0767c2bb80baa",
|
||||||
|
"@xata.io/kysely": "^0.2.1",
|
||||||
"bknd": "workspace:*",
|
"bknd": "workspace:*",
|
||||||
|
"kysely-neon": "^1.3.0",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
},
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"kysely": "^0.27.6",
|
||||||
|
"kysely-postgres-js": "^2.0.0",
|
||||||
|
"pg": "^8.14.0",
|
||||||
|
"postgres": "^3.4.7",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"packages/sqlocal": {
|
"packages/sqlocal": {
|
||||||
"name": "@bknd/sqlocal",
|
"name": "@bknd/sqlocal",
|
||||||
@@ -791,6 +796,8 @@
|
|||||||
|
|
||||||
"@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="],
|
"@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="],
|
||||||
|
|
||||||
|
"@neondatabase/serverless": ["@neondatabase/serverless@0.4.26", "", { "dependencies": { "@types/pg": "8.6.6" } }, "sha512-6DYEKos2GYn8NTgcJf33BLAx//LcgqzHVavQWe6ZkaDqmEq0I0Xtub6pzwFdq9iayNdCj7e2b0QKr5a8QKB8kQ=="],
|
||||||
|
|
||||||
"@next/env": ["@next/env@15.2.1", "", {}, "sha512-JmY0qvnPuS2NCWOz2bbby3Pe0VzdAQ7XpEB6uLIHmtXNfAsAO0KLQLkuAoc42Bxbo3/jMC3dcn9cdf+piCcG2Q=="],
|
"@next/env": ["@next/env@15.2.1", "", {}, "sha512-JmY0qvnPuS2NCWOz2bbby3Pe0VzdAQ7XpEB6uLIHmtXNfAsAO0KLQLkuAoc42Bxbo3/jMC3dcn9cdf+piCcG2Q=="],
|
||||||
|
|
||||||
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aWXT+5KEREoy3K5AKtiKwioeblmOvFFjd+F3dVleLvvLiQ/mD//jOOuUcx5hzcO9ISSw4lrqtUPntTpK32uXXQ=="],
|
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aWXT+5KEREoy3K5AKtiKwioeblmOvFFjd+F3dVleLvvLiQ/mD//jOOuUcx5hzcO9ISSw4lrqtUPntTpK32uXXQ=="],
|
||||||
@@ -1395,6 +1402,10 @@
|
|||||||
|
|
||||||
"@web3-storage/multipart-parser": ["@web3-storage/multipart-parser@1.0.0", "", {}, "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw=="],
|
"@web3-storage/multipart-parser": ["@web3-storage/multipart-parser@1.0.0", "", {}, "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw=="],
|
||||||
|
|
||||||
|
"@xata.io/client": ["@xata.io/client@0.0.0-next.v93343b9646f57a1e5c51c35eccf0767c2bb80baa", "", { "peerDependencies": { "typescript": ">=4.5" } }, "sha512-4Js4SAKwmmOPmZVIS1l2K8XVGGkUOi8L1jXuagDfeUX56n95wfA4xYMSmsVS0RLMmRWI4UM4bp5UcFJxwbFYGw=="],
|
||||||
|
|
||||||
|
"@xata.io/kysely": ["@xata.io/kysely@0.2.1", "", { "dependencies": { "@xata.io/client": "0.30.1" }, "peerDependencies": { "kysely": "*" } }, "sha512-0+WBcFkBSNEu11wVTyJyeNMOPUuolDKJMjXQr1nheHTNZLfsL0qKshTZOKIC/bGInjepGA7DQ/HFeKDHe5CDpA=="],
|
||||||
|
|
||||||
"@xyflow/react": ["@xyflow/react@12.4.4", "", { "dependencies": { "@xyflow/system": "0.0.52", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-9RZ9dgKZNJOlbrXXST5HPb5TcXPOIDGondjwcjDro44OQRPl1E0ZRPTeWPGaQtVjbg4WpR4BUYwOeshNI2TuVg=="],
|
"@xyflow/react": ["@xyflow/react@12.4.4", "", { "dependencies": { "@xyflow/system": "0.0.52", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-9RZ9dgKZNJOlbrXXST5HPb5TcXPOIDGondjwcjDro44OQRPl1E0ZRPTeWPGaQtVjbg4WpR4BUYwOeshNI2TuVg=="],
|
||||||
|
|
||||||
"@xyflow/system": ["@xyflow/system@0.0.52", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-pJBMaoh/GEebIABWEIxAai0yf57dm+kH7J/Br+LnLFPuJL87Fhcmm4KFWd/bCUy/kCWUg+2/yFAGY0AUHRPOnQ=="],
|
"@xyflow/system": ["@xyflow/system@0.0.52", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-pJBMaoh/GEebIABWEIxAai0yf57dm+kH7J/Br+LnLFPuJL87Fhcmm4KFWd/bCUy/kCWUg+2/yFAGY0AUHRPOnQ=="],
|
||||||
@@ -2543,6 +2554,10 @@
|
|||||||
|
|
||||||
"kysely-d1": ["kysely-d1@0.3.0", "", { "peerDependencies": { "kysely": "*" } }, "sha512-9wTbE6ooLiYtBa4wPg9e4fjfcmvRtgE/2j9pAjYrIq+iz+EsH/Hj9YbtxpEXA6JoRgfulVQ1EtGj6aycGGRpYw=="],
|
"kysely-d1": ["kysely-d1@0.3.0", "", { "peerDependencies": { "kysely": "*" } }, "sha512-9wTbE6ooLiYtBa4wPg9e4fjfcmvRtgE/2j9pAjYrIq+iz+EsH/Hj9YbtxpEXA6JoRgfulVQ1EtGj6aycGGRpYw=="],
|
||||||
|
|
||||||
|
"kysely-neon": ["kysely-neon@1.3.0", "", { "peerDependencies": { "@neondatabase/serverless": "^0.4.3", "kysely": "0.x.x", "ws": "^8.13.0" }, "optionalPeers": ["ws"] }, "sha512-CIIlbmqpIXVJDdBEYtEOwbmALag0jmqYrGfBeM4cHKb9AgBGs+X1SvXUZ8TqkDacQEqEZN2XtsDoUkcMIISjHw=="],
|
||||||
|
|
||||||
|
"kysely-postgres-js": ["kysely-postgres-js@2.0.0", "", { "peerDependencies": { "kysely": ">= 0.24.0 < 1", "postgres": ">= 3.4.0 < 4" } }, "sha512-R1tWx6/x3tSatWvsmbHJxpBZYhNNxcnMw52QzZaHKg7ZOWtHib4iZyEaw4gb2hNKVctWQ3jfMxZT/ZaEMK6kBQ=="],
|
||||||
|
|
||||||
"language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="],
|
"language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="],
|
||||||
|
|
||||||
"language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
|
"language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
|
||||||
@@ -2905,7 +2920,7 @@
|
|||||||
|
|
||||||
"pg-protocol": ["pg-protocol@1.8.0", "", {}, "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g=="],
|
"pg-protocol": ["pg-protocol@1.8.0", "", {}, "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g=="],
|
||||||
|
|
||||||
"pg-types": ["pg-types@4.0.2", "", { "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", "postgres-array": "~3.0.1", "postgres-bytea": "~3.0.0", "postgres-date": "~2.1.0", "postgres-interval": "^3.0.0", "postgres-range": "^1.1.1" } }, "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng=="],
|
"pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||||
|
|
||||||
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
|
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
|
||||||
|
|
||||||
@@ -2961,13 +2976,15 @@
|
|||||||
|
|
||||||
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
||||||
|
|
||||||
"postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="],
|
"postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="],
|
||||||
|
|
||||||
"postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="],
|
"postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
||||||
|
|
||||||
"postgres-date": ["postgres-date@2.1.0", "", {}, "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="],
|
"postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="],
|
||||||
|
|
||||||
"postgres-interval": ["postgres-interval@3.0.0", "", {}, "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="],
|
"postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
||||||
|
|
||||||
|
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||||
|
|
||||||
"postgres-range": ["postgres-range@1.1.4", "", {}, "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w=="],
|
"postgres-range": ["postgres-range@1.1.4", "", {}, "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w=="],
|
||||||
|
|
||||||
@@ -3847,8 +3864,6 @@
|
|||||||
|
|
||||||
"@babel/runtime/regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
|
"@babel/runtime/regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
|
||||||
|
|
||||||
"@bknd/postgres/@types/bun": ["@types/bun@1.2.5", "", { "dependencies": { "bun-types": "1.2.5" } }, "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg=="],
|
|
||||||
|
|
||||||
"@bundled-es-modules/cookie/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
"@bundled-es-modules/cookie/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||||
|
|
||||||
"@bundled-es-modules/tough-cookie/tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="],
|
"@bundled-es-modules/tough-cookie/tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="],
|
||||||
@@ -3911,6 +3926,8 @@
|
|||||||
|
|
||||||
"@libsql/kysely-libsql/@libsql/client": ["@libsql/client@0.8.1", "", { "dependencies": { "@libsql/core": "^0.8.1", "@libsql/hrana-client": "^0.6.2", "js-base64": "^3.7.5", "libsql": "^0.3.10", "promise-limit": "^2.7.0" } }, "sha512-xGg0F4iTDFpeBZ0r4pA6icGsYa5rG6RAG+i/iLDnpCAnSuTqEWMDdPlVseiq4Z/91lWI9jvvKKiKpovqJ1kZWA=="],
|
"@libsql/kysely-libsql/@libsql/client": ["@libsql/client@0.8.1", "", { "dependencies": { "@libsql/core": "^0.8.1", "@libsql/hrana-client": "^0.6.2", "js-base64": "^3.7.5", "libsql": "^0.3.10", "promise-limit": "^2.7.0" } }, "sha512-xGg0F4iTDFpeBZ0r4pA6icGsYa5rG6RAG+i/iLDnpCAnSuTqEWMDdPlVseiq4Z/91lWI9jvvKKiKpovqJ1kZWA=="],
|
||||||
|
|
||||||
|
"@neondatabase/serverless/@types/pg": ["@types/pg@8.6.6", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw=="],
|
||||||
|
|
||||||
"@plasmicapp/query/swr": ["swr@1.3.0", "", { "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw=="],
|
"@plasmicapp/query/swr": ["swr@1.3.0", "", { "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw=="],
|
||||||
|
|
||||||
"@remix-run/node/cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
"@remix-run/node/cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
||||||
@@ -4067,6 +4084,8 @@
|
|||||||
|
|
||||||
"@types/jest/pretty-format": ["pretty-format@25.5.0", "", { "dependencies": { "@jest/types": "^25.5.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" } }, "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ=="],
|
"@types/jest/pretty-format": ["pretty-format@25.5.0", "", { "dependencies": { "@jest/types": "^25.5.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" } }, "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ=="],
|
||||||
|
|
||||||
|
"@types/pg/pg-types": ["pg-types@4.0.2", "", { "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", "postgres-array": "~3.0.1", "postgres-bytea": "~3.0.0", "postgres-date": "~2.1.0", "postgres-interval": "^3.0.0", "postgres-range": "^1.1.1" } }, "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng=="],
|
||||||
|
|
||||||
"@typescript-eslint/experimental-utils/eslint-utils": ["eslint-utils@2.1.0", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg=="],
|
"@typescript-eslint/experimental-utils/eslint-utils": ["eslint-utils@2.1.0", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
"@typescript-eslint/typescript-estree/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||||
@@ -4127,6 +4146,8 @@
|
|||||||
|
|
||||||
"@wdio/utils/decamelize": ["decamelize@6.0.0", "", {}, "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA=="],
|
"@wdio/utils/decamelize": ["decamelize@6.0.0", "", {}, "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA=="],
|
||||||
|
|
||||||
|
"@xata.io/kysely/@xata.io/client": ["@xata.io/client@0.30.1", "", { "peerDependencies": { "typescript": ">=4.5" } }, "sha512-dAzDPHmIfenVIpF39m1elmW5ngjWu2mO8ZqJBN7dmYdXr98uhPANfLdVZnc3mUNG+NH37LqY1dSO862hIo2oRw=="],
|
||||||
|
|
||||||
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||||
|
|
||||||
"acorn-globals/acorn": ["acorn@6.4.2", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="],
|
"acorn-globals/acorn": ["acorn@6.4.2", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="],
|
||||||
@@ -4471,8 +4492,6 @@
|
|||||||
|
|
||||||
"peek-stream/duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="],
|
"peek-stream/duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="],
|
||||||
|
|
||||||
"pg/pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
|
||||||
|
|
||||||
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
|
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
|
||||||
|
|
||||||
"pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
"pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
||||||
@@ -4753,6 +4772,14 @@
|
|||||||
|
|
||||||
"@types/jest/pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
"@types/jest/pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"@types/pg/pg-types/postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="],
|
||||||
|
|
||||||
|
"@types/pg/pg-types/postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="],
|
||||||
|
|
||||||
|
"@types/pg/pg-types/postgres-date": ["postgres-date@2.1.0", "", {}, "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="],
|
||||||
|
|
||||||
|
"@types/pg/pg-types/postgres-interval": ["postgres-interval@3.0.0", "", {}, "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
"@typescript-eslint/typescript-estree/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||||
|
|
||||||
"@verdaccio/local-storage-legacy/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
|
"@verdaccio/local-storage-legacy/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
|
||||||
@@ -4945,14 +4972,6 @@
|
|||||||
|
|
||||||
"peek-stream/duplexify/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
"peek-stream/duplexify/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||||
|
|
||||||
"pg/pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
|
||||||
|
|
||||||
"pg/pg-types/postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="],
|
|
||||||
|
|
||||||
"pg/pg-types/postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
|
||||||
|
|
||||||
"pg/pg-types/postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
|
||||||
|
|
||||||
"progress-estimator/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
|
"progress-estimator/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
|
||||||
|
|
||||||
"progress-estimator/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
"progress-estimator/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
||||||
|
|||||||
@@ -3,13 +3,8 @@ title: 'Database'
|
|||||||
description: 'Choosing the right database configuration'
|
description: 'Choosing the right database configuration'
|
||||||
---
|
---
|
||||||
|
|
||||||
In order to use **bknd**, you need to prepare access information to your database and install
|
In order to use **bknd**, you need to prepare access information to your database and install the dependencies. Connections to the database are managed using Kysely. Therefore, all [its dialects](https://kysely.dev/docs/dialects) are theoretically supported.
|
||||||
the dependencies.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
Connections to the database are managed using Kysely. Therefore, all its dialects are
|
|
||||||
theoretically supported. However, only the `SQLite` dialect is implemented as of now.
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Database
|
## Database
|
||||||
### SQLite in-memory
|
### SQLite in-memory
|
||||||
@@ -56,7 +51,9 @@ connection object to your new database:
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Cloudflare D1
|
### Cloudflare D1
|
||||||
Using the [Cloudflare Adapter](/integration/cloudflare), you can choose to use a D1 database binding. To do so, you only need to add a D1 database to your `wrangler.toml` and it'll pick up automatically. To manually specify which D1 database to take, you can specify it manually:
|
Using the [Cloudflare Adapter](/integration/cloudflare), you can choose to use a D1 database binding. To do so, you only need to add a D1 database to your `wrangler.toml` and it'll pick up automatically.
|
||||||
|
|
||||||
|
To manually specify which D1 database to take, you can specify it explicitly:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { serve, d1 } from "bknd/adapter/cloudflare";
|
import { serve, d1 } from "bknd/adapter/cloudflare";
|
||||||
@@ -73,17 +70,19 @@ To use bknd with Postgres, you need to install the `@bknd/postgres` package. You
|
|||||||
npm install @bknd/postgres
|
npm install @bknd/postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
This package uses `pg` under the hood. If you'd like to see `postgres` or any other flavor, please create an [issue on Github](https://github.com/bknd-io/bknd/issues/new).
|
You can connect to your Postgres database using `pg` or `postgres` dialects. Additionally, you may also define your custom connection.
|
||||||
|
|
||||||
To establish a connection to your database, you can use any connection options available on the [`pg`](https://node-postgres.com/apis/client) package. Here is a quick example using the [Node.js Adapter](http://localhost:3000/integration/node):
|
#### Using `pg`
|
||||||
|
|
||||||
|
To establish a connection to your database, you can use any connection options available on the [`pg`](https://node-postgres.com/apis/client) package.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { serve } from "bknd/adapter/node";
|
import { serve } from "bknd/adapter/node";
|
||||||
import { PostgresConnection } from "@bknd/postgres";
|
import { pg } from "@bknd/postgres";
|
||||||
|
|
||||||
/** @type {import("bknd/adapter/node").NodeBkndConfig} */
|
/** @type {import("bknd/adapter/node").NodeBkndConfig} */
|
||||||
const config = {
|
const config = {
|
||||||
connection: new PostgresConnection({
|
connection: pg({
|
||||||
connectionString:
|
connectionString:
|
||||||
"postgresql://user:password@localhost:5432/database",
|
"postgresql://user:password@localhost:5432/database",
|
||||||
}),
|
}),
|
||||||
@@ -92,6 +91,64 @@ const config = {
|
|||||||
serve(config);
|
serve(config);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Using `postgres`
|
||||||
|
|
||||||
|
To establish a connection to your database, you can use any connection options available on the [`postgres`](https://github.com/porsager/postgres) package.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { serve } from "bknd/adapter/node";
|
||||||
|
import { postgresJs } from "@bknd/postgres";
|
||||||
|
|
||||||
|
serve({
|
||||||
|
connection: postgresJs("postgresql://user:password@localhost:5432/database"),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using custom connection
|
||||||
|
|
||||||
|
Several Postgres hosting providers offer their own clients to connect to their database, e.g. suitable for serverless environments.
|
||||||
|
|
||||||
|
Example using `@neondatabase/serverless`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||||
|
import { NeonDialect } from "kysely-neon";
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(NeonDialect)({
|
||||||
|
connectionString: process.env.NEON,
|
||||||
|
});
|
||||||
|
|
||||||
|
serve({
|
||||||
|
connection: connection,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Example using `@xata.io/client`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||||
|
import { XataDialect } from "@xata.io/kysely";
|
||||||
|
import { buildClient } from "@xata.io/client";
|
||||||
|
|
||||||
|
const client = buildClient();
|
||||||
|
const xata = new client({
|
||||||
|
databaseURL: process.env.XATA_URL,
|
||||||
|
apiKey: process.env.XATA_API_KEY,
|
||||||
|
branch: process.env.XATA_BRANCH,
|
||||||
|
});
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(XataDialect, {
|
||||||
|
supports: {
|
||||||
|
batching: false,
|
||||||
|
},
|
||||||
|
})({ xata });
|
||||||
|
|
||||||
|
serve({
|
||||||
|
connection: connection,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### SQLocal
|
### SQLocal
|
||||||
To use bknd with `sqlocal` for a offline expierence, you need to install the `@bknd/sqlocal` package. You can do so by running the following command:
|
To use bknd with `sqlocal` for a offline expierence, you need to install the `@bknd/sqlocal` package. You can do so by running the following command:
|
||||||
|
|
||||||
@@ -138,7 +195,11 @@ const app = createApp({ connection })
|
|||||||
## Initial Structure
|
## Initial Structure
|
||||||
To provide an initial database structure, you can pass `initialConfig` to the creation of an app. This will only be used if there isn't an existing configuration found in the database given. Here is a quick example:
|
To provide an initial database structure, you can pass `initialConfig` to the creation of an app. This will only be used if there isn't an existing configuration found in the database given. Here is a quick example:
|
||||||
|
|
||||||
```ts
|
<Note>
|
||||||
|
The initial structure is only respected if the database is empty! If you made updates, ensure to delete the database first, or perform updates through the Admin UI.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
```typescript
|
||||||
import { createApp } from "bknd";
|
import { createApp } from "bknd";
|
||||||
import { em, entity, text, number } from "bknd/data";
|
import { em, entity, text, number } from "bknd/data";
|
||||||
|
|
||||||
@@ -193,13 +254,17 @@ Note that we didn't add relational fields directly to the entity, but instead de
|
|||||||
</Note>
|
</Note>
|
||||||
|
|
||||||
### Type completion
|
### Type completion
|
||||||
|
To get type completion, there are two options:
|
||||||
|
1. Use the CLI to [generate the types](/usage/cli#generating-types-types)
|
||||||
|
2. If you have an initial structure created with the prototype functions, you can extend the `DB` interface with your own schema.
|
||||||
|
|
||||||
All entity related functions use the types defined in `DB` from `bknd/core`. To get type completion, you can extend that interface with your own schema:
|
All entity related functions use the types defined in `DB` from `bknd/core`. To get type completion, you can extend that interface with your own schema:
|
||||||
|
|
||||||
```ts
|
```typescript
|
||||||
import { em } from "bknd/data";
|
import { em } from "bknd/data";
|
||||||
import { Api } from "bknd/client";
|
import { Api } from "bknd/client";
|
||||||
|
|
||||||
const schema = em({ /* ... */ });
|
const schema = em({ /* ... */ });
|
||||||
|
|
||||||
type Database = (typeof schema)["DB"];
|
type Database = (typeof schema)["DB"];
|
||||||
declare module "bknd/core" {
|
declare module "bknd/core" {
|
||||||
@@ -217,10 +282,12 @@ The type completion is available for the API as well as all provided [React hook
|
|||||||
To seed your database with initial data, you can pass a `seed` function to the configuration. It
|
To seed your database with initial data, you can pass a `seed` function to the configuration. It
|
||||||
provides the `ModuleBuildContext` as the first argument.
|
provides the `ModuleBuildContext` as the first argument.
|
||||||
|
|
||||||
Note that the seed function will only be executed on app's first boot. If a configuration
|
<Note>
|
||||||
already exists in the database, it will not be executed.
|
Note that the seed function will only be executed on app's first boot. If a configuration
|
||||||
|
already exists in the database, it will not be executed.
|
||||||
|
</Note>
|
||||||
|
|
||||||
```ts
|
```typescript
|
||||||
import { createApp, type ModuleBuildContext } from "bknd";
|
import { createApp, type ModuleBuildContext } from "bknd";
|
||||||
|
|
||||||
const app = createApp({
|
const app = createApp({
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
# Postgres adapter for `bknd` (experimental)
|
# Postgres adapter for `bknd` (experimental)
|
||||||
This packages adds an adapter to use a Postgres database with [`bknd`](https://github.com/bknd-io/bknd). It is based on [`pg`](https://github.com/brianc/node-postgres) and the driver included in [`kysely`](https://github.com/kysely-org/kysely).
|
This packages adds an adapter to use a Postgres database with [`bknd`](https://github.com/bknd-io/bknd). It works with both `pg` and `postgres` drivers, and supports custom postgres connections.
|
||||||
|
* works with any Postgres database (tested with Supabase, Neon, Xata, and RDS)
|
||||||
|
* choose between `pg` and `postgres` drivers
|
||||||
|
* create custom postgres connections with any kysely postgres dialect
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Install the adapter with:
|
Install the adapter with:
|
||||||
@@ -7,44 +10,93 @@ Install the adapter with:
|
|||||||
npm install @bknd/postgres
|
npm install @bknd/postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Using `pg` driver
|
||||||
|
Install the [`pg`](https://github.com/brianc/node-postgres) driver with:
|
||||||
|
```bash
|
||||||
|
npm install pg
|
||||||
|
```
|
||||||
|
|
||||||
Create a connection:
|
Create a connection:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { PostgresConnection } from "@bknd/postgres";
|
import { pg } from "@bknd/postgres";
|
||||||
|
|
||||||
const connection = new PostgresConnection({
|
// accepts `pg` configuration
|
||||||
|
const connection = pg({
|
||||||
host: "localhost",
|
host: "localhost",
|
||||||
port: 5432,
|
port: 5432,
|
||||||
user: "postgres",
|
user: "postgres",
|
||||||
password: "postgres",
|
password: "postgres",
|
||||||
database: "bknd",
|
database: "postgres",
|
||||||
|
});
|
||||||
|
|
||||||
|
// or with a connection string
|
||||||
|
const connection = pg({
|
||||||
|
connectionString: "postgres://postgres:postgres@localhost:5432/postgres",
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Use the connection depending on which framework or runtime you are using. E.g., when using `createApp`, you can use the connection as follows:
|
## Using `postgres` driver
|
||||||
|
|
||||||
```ts
|
Install the [`postgres`](https://github.com/porsager/postgres) driver with:
|
||||||
import { createApp } from "bknd";
|
```bash
|
||||||
import { PostgresConnection } from "@bknd/postgres";
|
npm install postgres
|
||||||
|
|
||||||
const connection = new PostgresConnection();
|
|
||||||
const app = createApp({ connection });
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Or if you're using it with a framework, say Next.js, you can add the connection object to where you're initializating the app:
|
Create a connection:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// e.g. in src/app/api/[[...bknd]]/route.ts
|
import { postgresJs } from "@bknd/postgres";
|
||||||
import { serve } from "bknd/adapter/nextjs";
|
|
||||||
import { PostgresConnection } from "@bknd/postgres";
|
|
||||||
|
|
||||||
const connection = new PostgresConnection();
|
// accepts `postgres` configuration
|
||||||
const handler = serve({
|
const connection = postgresJs("postgres://postgres:postgres@localhost:5432/postgres");
|
||||||
connection
|
|
||||||
})
|
|
||||||
|
|
||||||
// ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information about how to integrate Next.js in general, check out the [Next.js documentation](https://docs.bknd.io/integration/nextjs).
|
## Using custom postgres dialects
|
||||||
|
|
||||||
|
You can create a custom kysely postgres dialect by using the `createCustomPostgresConnection` function.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(MyDialect)({
|
||||||
|
// your custom dialect configuration
|
||||||
|
supports: {
|
||||||
|
batching: true
|
||||||
|
},
|
||||||
|
excludeTables: ["my_table"],
|
||||||
|
plugins: [new MyKyselyPlugin()],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom `neon` connection
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||||
|
import { NeonDialect } from "kysely-neon";
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(NeonDialect)({
|
||||||
|
connectionString: process.env.NEON,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom `xata` connection
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||||
|
import { XataDialect } from "@xata.io/kysely";
|
||||||
|
import { buildClient } from "@xata.io/client";
|
||||||
|
|
||||||
|
const client = buildClient();
|
||||||
|
const xata = new client({
|
||||||
|
databaseURL: process.env.XATA_URL,
|
||||||
|
apiKey: process.env.XATA_API_KEY,
|
||||||
|
branch: process.env.XATA_BRANCH,
|
||||||
|
});
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(XataDialect, {
|
||||||
|
supports: {
|
||||||
|
batching: false,
|
||||||
|
},
|
||||||
|
})({ xata });
|
||||||
|
```
|
||||||
14
packages/postgres/examples/neon.ts
Normal file
14
packages/postgres/examples/neon.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { serve } from "bknd/adapter/bun";
|
||||||
|
import { createCustomPostgresConnection } from "../src";
|
||||||
|
import { NeonDialect } from "kysely-neon";
|
||||||
|
|
||||||
|
const neon = createCustomPostgresConnection(NeonDialect);
|
||||||
|
|
||||||
|
export default serve({
|
||||||
|
connection: neon({
|
||||||
|
connectionString: process.env.NEON,
|
||||||
|
}),
|
||||||
|
// ignore this, it's only required within this repository
|
||||||
|
// because bknd is installed via "workspace:*"
|
||||||
|
distPath: "../../app/dist",
|
||||||
|
});
|
||||||
24
packages/postgres/examples/xata.ts
Normal file
24
packages/postgres/examples/xata.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { serve } from "bknd/adapter/bun";
|
||||||
|
import { createCustomPostgresConnection } from "../src";
|
||||||
|
import { XataDialect } from "@xata.io/kysely";
|
||||||
|
import { buildClient } from "@xata.io/client";
|
||||||
|
|
||||||
|
const client = buildClient();
|
||||||
|
const xata = new client({
|
||||||
|
databaseURL: process.env.XATA_URL,
|
||||||
|
apiKey: process.env.XATA_API_KEY,
|
||||||
|
branch: process.env.XATA_BRANCH,
|
||||||
|
});
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(XataDialect, {
|
||||||
|
supports: {
|
||||||
|
batching: false,
|
||||||
|
},
|
||||||
|
})({ xata });
|
||||||
|
|
||||||
|
export default serve({
|
||||||
|
connection,
|
||||||
|
// ignore this, it's only required within this repository
|
||||||
|
// because bknd is installed via "workspace:*"
|
||||||
|
distPath: "../../../app/dist",
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bknd/postgres",
|
"name": "@bknd/postgres",
|
||||||
"version": "0.0.1",
|
"version": "0.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "dist/index.js",
|
"module": "dist/index.js",
|
||||||
@@ -17,15 +17,20 @@
|
|||||||
"docker:start": "docker run --rm --name bknd-test-postgres -d -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres -e POSTGRES_DB=bknd -p 5430:5432 postgres:17",
|
"docker:start": "docker run --rm --name bknd-test-postgres -d -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres -e POSTGRES_DB=bknd -p 5430:5432 postgres:17",
|
||||||
"docker:stop": "docker stop bknd-test-postgres"
|
"docker:stop": "docker stop bknd-test-postgres"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"optionalDependencies": {
|
||||||
|
"kysely": "^0.27.6",
|
||||||
|
"kysely-postgres-js": "^2.0.0",
|
||||||
"pg": "^8.14.0",
|
"pg": "^8.14.0",
|
||||||
"kysely": "^0.27.6"
|
"postgres": "^3.4.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.2.5",
|
"@types/bun": "^1.2.5",
|
||||||
"@types/node": "^22.13.10",
|
"@types/node": "^22.13.10",
|
||||||
"@types/pg": "^8.11.11",
|
"@types/pg": "^8.11.11",
|
||||||
|
"@xata.io/client": "^0.0.0-next.v93343b9646f57a1e5c51c35eccf0767c2bb80baa",
|
||||||
|
"@xata.io/kysely": "^0.2.1",
|
||||||
"bknd": "workspace:*",
|
"bknd": "workspace:*",
|
||||||
|
"kysely-neon": "^1.3.0",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2"
|
"typescript": "^5.8.2"
|
||||||
},
|
},
|
||||||
@@ -33,10 +38,11 @@
|
|||||||
"entry": ["src/index.ts"],
|
"entry": ["src/index.ts"],
|
||||||
"format": ["esm"],
|
"format": ["esm"],
|
||||||
"target": "es2022",
|
"target": "es2022",
|
||||||
|
"metafile": true,
|
||||||
"clean": true,
|
"clean": true,
|
||||||
"minify": true,
|
"minify": true,
|
||||||
"dts": true,
|
"dts": true,
|
||||||
"external": ["bknd", "pg", "kysely"]
|
"external": ["bknd", "pg", "postgres", "kysely", "kysely-postgres-js"]
|
||||||
},
|
},
|
||||||
"files": ["dist", "README.md", "!*.map", "!metafile*.json"]
|
"files": ["dist", "README.md", "!*.map", "!metafile*.json"]
|
||||||
}
|
}
|
||||||
|
|||||||
32
packages/postgres/src/PgPostgresConnection.ts
Normal file
32
packages/postgres/src/PgPostgresConnection.ts
Normal 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);
|
||||||
|
}
|
||||||
@@ -1,61 +1,44 @@
|
|||||||
import { Connection, type FieldSpec, type SchemaResponse } from "bknd/data";
|
import { Connection, type DbFunctions, type FieldSpec, type SchemaResponse } from "bknd/data";
|
||||||
import {
|
import {
|
||||||
|
ParseJSONResultsPlugin,
|
||||||
type ColumnDataType,
|
type ColumnDataType,
|
||||||
type ColumnDefinitionBuilder,
|
type ColumnDefinitionBuilder,
|
||||||
type DatabaseIntrospector,
|
type Kysely,
|
||||||
Kysely,
|
type KyselyPlugin,
|
||||||
ParseJSONResultsPlugin,
|
|
||||||
PostgresDialect,
|
|
||||||
type SelectQueryBuilder,
|
type SelectQueryBuilder,
|
||||||
} from "kysely";
|
} from "kysely";
|
||||||
import { jsonArrayFrom, jsonBuildObject, jsonObjectFrom } from "kysely/helpers/postgres";
|
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>;
|
export type QB = SelectQueryBuilder<any, any, any>;
|
||||||
|
|
||||||
const plugins = [new ParseJSONResultsPlugin()];
|
export const plugins = [new ParseJSONResultsPlugin()];
|
||||||
|
|
||||||
class CustomPostgresDialect extends PostgresDialect {
|
export abstract class PostgresConnection<DB = any> extends Connection<DB> {
|
||||||
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
|
||||||
return new PostgresIntrospector(db, {
|
|
||||||
excludeTables: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PostgresConnection extends Connection {
|
|
||||||
protected override readonly supported = {
|
protected override readonly supported = {
|
||||||
batching: true,
|
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(
|
super(
|
||||||
kysely,
|
kysely,
|
||||||
{
|
fn ?? {
|
||||||
jsonArrayFrom,
|
jsonArrayFrom,
|
||||||
jsonBuildObject,
|
jsonBuildObject,
|
||||||
jsonObjectFrom,
|
jsonObjectFrom,
|
||||||
},
|
},
|
||||||
plugins,
|
_plugins ?? plugins,
|
||||||
);
|
);
|
||||||
this.pool = pool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override getFieldSchema(spec: FieldSpec): SchemaResponse {
|
override getFieldSchema(spec: FieldSpec): SchemaResponse {
|
||||||
this.validateFieldSpecType(spec.type);
|
this.validateFieldSpecType(spec.type);
|
||||||
let type: ColumnDataType = spec.primary ? "serial" : spec.type;
|
let type: ColumnDataType = spec.type;
|
||||||
|
|
||||||
|
if (spec.primary) {
|
||||||
|
if (spec.type === "integer") {
|
||||||
|
type = "serial";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (spec.type) {
|
switch (spec.type) {
|
||||||
case "blob":
|
case "blob":
|
||||||
@@ -90,10 +73,6 @@ export class PostgresConnection extends Connection {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
override async close(): Promise<void> {
|
|
||||||
await this.pool.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async batch<Queries extends QB[]>(
|
protected override async batch<Queries extends QB[]>(
|
||||||
queries: [...Queries],
|
queries: [...Queries],
|
||||||
): Promise<{
|
): Promise<{
|
||||||
|
|||||||
41
packages/postgres/src/PostgresJsConnection.ts
Normal file
41
packages/postgres/src/PostgresJsConnection.ts
Normal 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 });
|
||||||
|
}
|
||||||
43
packages/postgres/src/custom.ts
Normal file
43
packages/postgres/src/custom.ts
Normal 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);
|
||||||
|
}
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
export { PostgresConnection, type PostgresConnectionConfig } from "./PostgresConnection";
|
export { pg, PgPostgresConnection, type PgPostgresConnectionConfig } from "./PgPostgresConnection";
|
||||||
export { PostgresIntrospector } from "./PostgresIntrospector";
|
export { PostgresIntrospector } from "./PostgresIntrospector";
|
||||||
|
export { PostgresConnection, type QB, plugins } from "./PostgresConnection";
|
||||||
|
export { postgresJs, PostgresJsConnection, type PostgresJsConfig } from "./PostgresJsConnection";
|
||||||
|
export { createCustomPostgresConnection } from "./custom";
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import { describe, it, expect } from "bun:test";
|
|
||||||
|
|
||||||
import { PostgresConnection } from "../src";
|
|
||||||
import { createConnection, cleanDatabase } from "./setup";
|
|
||||||
|
|
||||||
describe(PostgresConnection, () => {
|
|
||||||
it("should connect to the database", async () => {
|
|
||||||
const connection = createConnection();
|
|
||||||
expect(await connection.ping()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should clean the database", async () => {
|
|
||||||
const connection = createConnection();
|
|
||||||
await cleanDatabase(connection);
|
|
||||||
|
|
||||||
const tables = await connection.getIntrospector().getTables();
|
|
||||||
expect(tables).toEqual([]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
import { describe, it, expect, beforeAll, afterAll, afterEach } from "bun:test";
|
|
||||||
|
|
||||||
import { createApp } from "bknd";
|
|
||||||
import * as proto from "bknd/data";
|
|
||||||
|
|
||||||
import { createConnection, cleanDatabase } from "./setup";
|
|
||||||
import type { PostgresConnection } from "../src";
|
|
||||||
|
|
||||||
let connection: PostgresConnection;
|
|
||||||
beforeAll(async () => {
|
|
||||||
connection = createConnection();
|
|
||||||
await cleanDatabase(connection);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await cleanDatabase(connection);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
await connection.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("integration", () => {
|
|
||||||
it("should create app and ping", async () => {
|
|
||||||
const app = createApp({
|
|
||||||
connection,
|
|
||||||
});
|
|
||||||
await app.build();
|
|
||||||
|
|
||||||
expect(app.version()).toBeDefined();
|
|
||||||
expect(await app.em.ping()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should create a basic schema", async () => {
|
|
||||||
const schema = proto.em(
|
|
||||||
{
|
|
||||||
posts: proto.entity("posts", {
|
|
||||||
title: proto.text().required(),
|
|
||||||
content: proto.text(),
|
|
||||||
}),
|
|
||||||
comments: proto.entity("comments", {
|
|
||||||
content: proto.text(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
(fns, s) => {
|
|
||||||
fns.relation(s.comments).manyToOne(s.posts);
|
|
||||||
fns.index(s.posts).on(["title"], true);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const app = createApp({
|
|
||||||
connection,
|
|
||||||
initialConfig: {
|
|
||||||
data: schema.toJSON(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await app.build();
|
|
||||||
|
|
||||||
expect(app.em.entities.length).toBe(2);
|
|
||||||
expect(app.em.entities.map((e) => e.name)).toEqual(["posts", "comments"]);
|
|
||||||
|
|
||||||
const api = app.getApi();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
(
|
|
||||||
await api.data.createMany("posts", [
|
|
||||||
{
|
|
||||||
title: "Hello",
|
|
||||||
content: "World",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Hello 2",
|
|
||||||
content: "World 2",
|
|
||||||
},
|
|
||||||
])
|
|
||||||
).data,
|
|
||||||
).toEqual([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: "Hello",
|
|
||||||
content: "World",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: "Hello 2",
|
|
||||||
content: "World 2",
|
|
||||||
},
|
|
||||||
] as any);
|
|
||||||
|
|
||||||
// try to create an existing
|
|
||||||
expect(
|
|
||||||
(
|
|
||||||
await api.data.createOne("posts", {
|
|
||||||
title: "Hello",
|
|
||||||
})
|
|
||||||
).ok,
|
|
||||||
).toBe(false);
|
|
||||||
|
|
||||||
// add a comment to a post
|
|
||||||
await api.data.createOne("comments", {
|
|
||||||
content: "Hello",
|
|
||||||
posts_id: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
// and then query using a `with` property
|
|
||||||
const result = await api.data.readMany("posts", { with: ["comments"] });
|
|
||||||
expect(result.length).toBe(2);
|
|
||||||
expect(result[0].comments.length).toBe(1);
|
|
||||||
expect(result[0].comments[0].content).toBe("Hello");
|
|
||||||
expect(result[1].comments.length).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
16
packages/postgres/test/pg.test.ts
Normal file
16
packages/postgres/test/pg.test.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { describe } from "bun:test";
|
||||||
|
import { pg } from "../src/PgPostgresConnection";
|
||||||
|
import { testSuite } from "./suite";
|
||||||
|
|
||||||
|
describe("pg", () => {
|
||||||
|
testSuite({
|
||||||
|
createConnection: () =>
|
||||||
|
pg({
|
||||||
|
host: "localhost",
|
||||||
|
port: 5430,
|
||||||
|
user: "postgres",
|
||||||
|
password: "postgres",
|
||||||
|
database: "bknd",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
16
packages/postgres/test/postgresjs.test.ts
Normal file
16
packages/postgres/test/postgresjs.test.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { describe } from "bun:test";
|
||||||
|
import { postgresJs } from "../src/PostgresJsConnection";
|
||||||
|
import { testSuite } from "./suite";
|
||||||
|
|
||||||
|
describe("postgresjs", () => {
|
||||||
|
testSuite({
|
||||||
|
createConnection: () =>
|
||||||
|
postgresJs({
|
||||||
|
host: "localhost",
|
||||||
|
port: 5430,
|
||||||
|
user: "postgres",
|
||||||
|
password: "postgres",
|
||||||
|
database: "bknd",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import type { Kysely } from "kysely";
|
|
||||||
import { PostgresConnection, PostgresIntrospector, type PostgresConnectionConfig } from "../src";
|
|
||||||
|
|
||||||
export const info = {
|
|
||||||
host: "localhost",
|
|
||||||
port: 5430,
|
|
||||||
user: "postgres",
|
|
||||||
password: "postgres",
|
|
||||||
database: "bknd",
|
|
||||||
};
|
|
||||||
|
|
||||||
export function createConnection(config: PostgresConnectionConfig = {}) {
|
|
||||||
return new PostgresConnection({
|
|
||||||
...info,
|
|
||||||
...config,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function cleanDatabase(connection: PostgresConnection) {
|
|
||||||
const kysely = connection.kysely;
|
|
||||||
|
|
||||||
// drop all tables & create new schema
|
|
||||||
await kysely.schema.dropSchema("public").ifExists().cascade().execute();
|
|
||||||
await kysely.schema.createSchema("public").execute();
|
|
||||||
}
|
|
||||||
197
packages/postgres/test/suite.ts
Normal file
197
packages/postgres/test/suite.ts
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import { describe, beforeAll, afterAll, expect, it, afterEach } from "bun:test";
|
||||||
|
import type { PostgresConnection } from "../src";
|
||||||
|
import { createApp } from "bknd";
|
||||||
|
import * as proto from "bknd/data";
|
||||||
|
import { disableConsoleLog, enableConsoleLog } from "bknd/utils";
|
||||||
|
|
||||||
|
export type TestSuiteConfig = {
|
||||||
|
createConnection: () => InstanceType<typeof PostgresConnection>;
|
||||||
|
cleanDatabase?: (connection: InstanceType<typeof PostgresConnection>) => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function defaultCleanDatabase(connection: InstanceType<typeof PostgresConnection>) {
|
||||||
|
const kysely = connection.kysely;
|
||||||
|
|
||||||
|
// drop all tables & create new schema
|
||||||
|
await kysely.schema.dropSchema("public").ifExists().cascade().execute();
|
||||||
|
await kysely.schema.createSchema("public").execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cleanDatabase(
|
||||||
|
connection: InstanceType<typeof PostgresConnection>,
|
||||||
|
config: TestSuiteConfig,
|
||||||
|
) {
|
||||||
|
if (config.cleanDatabase) {
|
||||||
|
await config.cleanDatabase(connection);
|
||||||
|
} else {
|
||||||
|
await defaultCleanDatabase(connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function testSuite(config: TestSuiteConfig) {
|
||||||
|
beforeAll(() => disableConsoleLog(["log", "warn", "error"]));
|
||||||
|
afterAll(() => enableConsoleLog());
|
||||||
|
|
||||||
|
describe("base", () => {
|
||||||
|
it("should connect to the database", async () => {
|
||||||
|
const connection = config.createConnection();
|
||||||
|
expect(await connection.ping()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should clean the database", async () => {
|
||||||
|
const connection = config.createConnection();
|
||||||
|
await cleanDatabase(connection, config);
|
||||||
|
|
||||||
|
const tables = await connection.getIntrospector().getTables();
|
||||||
|
expect(tables).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("integration", () => {
|
||||||
|
let connection: PostgresConnection;
|
||||||
|
beforeAll(async () => {
|
||||||
|
connection = config.createConnection();
|
||||||
|
await cleanDatabase(connection, config);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await cleanDatabase(connection, config);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await connection.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create app and ping", async () => {
|
||||||
|
const app = createApp({
|
||||||
|
connection,
|
||||||
|
});
|
||||||
|
await app.build();
|
||||||
|
|
||||||
|
expect(app.version()).toBeDefined();
|
||||||
|
expect(await app.em.ping()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a basic schema", async () => {
|
||||||
|
const schema = proto.em(
|
||||||
|
{
|
||||||
|
posts: proto.entity("posts", {
|
||||||
|
title: proto.text().required(),
|
||||||
|
content: proto.text(),
|
||||||
|
}),
|
||||||
|
comments: proto.entity("comments", {
|
||||||
|
content: proto.text(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
(fns, s) => {
|
||||||
|
fns.relation(s.comments).manyToOne(s.posts);
|
||||||
|
fns.index(s.posts).on(["title"], true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const app = createApp({
|
||||||
|
connection,
|
||||||
|
initialConfig: {
|
||||||
|
data: schema.toJSON(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.build();
|
||||||
|
|
||||||
|
expect(app.em.entities.length).toBe(2);
|
||||||
|
expect(app.em.entities.map((e) => e.name)).toEqual(["posts", "comments"]);
|
||||||
|
|
||||||
|
const api = app.getApi();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
await api.data.createMany("posts", [
|
||||||
|
{
|
||||||
|
title: "Hello",
|
||||||
|
content: "World",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Hello 2",
|
||||||
|
content: "World 2",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).data,
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Hello",
|
||||||
|
content: "World",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Hello 2",
|
||||||
|
content: "World 2",
|
||||||
|
},
|
||||||
|
] as any);
|
||||||
|
|
||||||
|
// try to create an existing
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
await api.data.createOne("posts", {
|
||||||
|
title: "Hello",
|
||||||
|
})
|
||||||
|
).ok,
|
||||||
|
).toBe(false);
|
||||||
|
|
||||||
|
// add a comment to a post
|
||||||
|
await api.data.createOne("comments", {
|
||||||
|
content: "Hello",
|
||||||
|
posts_id: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
// and then query using a `with` property
|
||||||
|
const result = await api.data.readMany("posts", { with: ["comments"] });
|
||||||
|
expect(result.length).toBe(2);
|
||||||
|
expect(result[0].comments.length).toBe(1);
|
||||||
|
expect(result[0].comments[0].content).toBe("Hello");
|
||||||
|
expect(result[1].comments.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should support uuid", async () => {
|
||||||
|
const schema = proto.em(
|
||||||
|
{
|
||||||
|
posts: proto.entity(
|
||||||
|
"posts",
|
||||||
|
{
|
||||||
|
title: proto.text().required(),
|
||||||
|
content: proto.text(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
primary_format: "uuid",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
comments: proto.entity("comments", {
|
||||||
|
content: proto.text(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
(fns, s) => {
|
||||||
|
fns.relation(s.comments).manyToOne(s.posts);
|
||||||
|
fns.index(s.posts).on(["title"], true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const app = createApp({
|
||||||
|
connection,
|
||||||
|
initialConfig: {
|
||||||
|
data: schema.toJSON(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.build();
|
||||||
|
const config = app.toJSON();
|
||||||
|
// @ts-expect-error
|
||||||
|
expect(config.data.entities?.posts.fields?.id.config?.format).toBe("uuid");
|
||||||
|
|
||||||
|
const em = app.em;
|
||||||
|
const mutator = em.mutator(em.entity("posts"));
|
||||||
|
const data = await mutator.insertOne({ title: "Hello", content: "World" });
|
||||||
|
expect(data.data.id).toBeString();
|
||||||
|
expect(String(data.data.id).length).toBe(36);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user