mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-02 08:06:00 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b44b87923b | |||
| 6e6c65b375 | |||
| c9c00adf6c | |||
| ee15150c78 | |||
| f8f5ef9c98 | |||
| ec015b7849 | |||
| 67e0374c04 | |||
| 9380091ba9 | |||
| aa66a81b27 | |||
| 0c15ec1434 | |||
| 7b8c7f1ae4 |
+1
-1
@@ -3,7 +3,7 @@
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"bin": "./dist/cli/index.js",
|
||||
"version": "0.10.0-rc.5",
|
||||
"version": "0.10.0",
|
||||
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
|
||||
"homepage": "https://bknd.io",
|
||||
"repository": {
|
||||
|
||||
@@ -12,7 +12,7 @@ export type D1ConnectionConfig = {
|
||||
class CustomD1Dialect extends D1Dialect {
|
||||
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
||||
return new SqliteIntrospector(db, {
|
||||
excludeTables: ["_cf_KV"],
|
||||
excludeTables: ["_cf_KV", "_cf_METADATA"],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,10 @@ export class D1Connection extends SqliteConnection {
|
||||
super(kysely, {}, plugins);
|
||||
}
|
||||
|
||||
get client(): D1Database {
|
||||
return this.config.binding;
|
||||
}
|
||||
|
||||
protected override async batch<Queries extends QB[]>(
|
||||
queries: [...Queries],
|
||||
): Promise<{
|
||||
|
||||
@@ -167,7 +167,9 @@ export class Mutator<
|
||||
|
||||
const res = await this.single(query);
|
||||
|
||||
await this.emgr.emit(new Mutator.Events.MutatorInsertAfter({ entity, data: res.data }));
|
||||
await this.emgr.emit(
|
||||
new Mutator.Events.MutatorInsertAfter({ entity, data: res.data, changed: validatedData }),
|
||||
);
|
||||
|
||||
return res as any;
|
||||
}
|
||||
@@ -198,7 +200,12 @@ export class Mutator<
|
||||
const res = await this.single(query);
|
||||
|
||||
await this.emgr.emit(
|
||||
new Mutator.Events.MutatorUpdateAfter({ entity, entityId: id, data: res.data }),
|
||||
new Mutator.Events.MutatorUpdateAfter({
|
||||
entity,
|
||||
entityId: id,
|
||||
data: res.data,
|
||||
changed: validatedData,
|
||||
}),
|
||||
);
|
||||
|
||||
return res as any;
|
||||
|
||||
@@ -18,7 +18,11 @@ export class MutatorInsertBefore extends Event<{ entity: Entity; data: EntityDat
|
||||
});
|
||||
}
|
||||
}
|
||||
export class MutatorInsertAfter extends Event<{ entity: Entity; data: EntityData }> {
|
||||
export class MutatorInsertAfter extends Event<{
|
||||
entity: Entity;
|
||||
data: EntityData;
|
||||
changed: EntityData;
|
||||
}> {
|
||||
static override slug = "mutator-insert-after";
|
||||
}
|
||||
export class MutatorUpdateBefore extends Event<
|
||||
@@ -48,6 +52,7 @@ export class MutatorUpdateAfter extends Event<{
|
||||
entity: Entity;
|
||||
entityId: PrimaryFieldType;
|
||||
data: EntityData;
|
||||
changed: EntityData;
|
||||
}> {
|
||||
static override slug = "mutator-update-after";
|
||||
}
|
||||
|
||||
@@ -118,14 +118,20 @@ export class StorageS3Adapter extends AwsClient implements StorageAdapter {
|
||||
const res = await this.fetch(url, {
|
||||
method: "PUT",
|
||||
body,
|
||||
headers: isFile(body)
|
||||
? {
|
||||
// required for node environments
|
||||
"Content-Length": String(body.size),
|
||||
}
|
||||
: {},
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
// "df20fcb574dba1446cf5ec997940492b"
|
||||
return String(res.headers.get("etag"));
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to upload object: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
// "df20fcb574dba1446cf5ec997940492b"
|
||||
return String(res.headers.get("etag"));
|
||||
}
|
||||
|
||||
private async headObject(
|
||||
|
||||
@@ -22,20 +22,17 @@ type CanvasProps = ReactFlowProps & {
|
||||
onDropNewEdge?: (base: any) => any;
|
||||
};
|
||||
|
||||
export function Canvas(
|
||||
{
|
||||
nodes: _nodes,
|
||||
edges: _edges,
|
||||
externalProvider,
|
||||
backgroundStyle = "lines",
|
||||
minimap = false,
|
||||
children,
|
||||
onDropNewNode,
|
||||
onDropNewEdge,
|
||||
...props
|
||||
}: CanvasProps,
|
||||
ref?: any,
|
||||
) {
|
||||
export function Canvas({
|
||||
nodes: _nodes,
|
||||
edges: _edges,
|
||||
externalProvider,
|
||||
backgroundStyle = "lines",
|
||||
minimap = false,
|
||||
children,
|
||||
onDropNewNode,
|
||||
onDropNewEdge,
|
||||
...props
|
||||
}: CanvasProps) {
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState(_nodes ?? []);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState(_edges ?? []);
|
||||
const { screenToFlowPosition } = useReactFlow();
|
||||
@@ -179,6 +176,7 @@ export function Canvas(
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
nodesConnectable={false}
|
||||
/*panOnDrag={isSpacePressed}*/
|
||||
panOnDrag={true}
|
||||
zoomOnScroll={isCommandPressed}
|
||||
panOnScroll={!isCommandPressed}
|
||||
|
||||
@@ -44,10 +44,8 @@ export const layoutWithDagre = ({ nodes, edges, graph }: LayoutProps) => {
|
||||
const position = dagreGraph.node(node.id);
|
||||
return {
|
||||
...node,
|
||||
position: {
|
||||
x: position.x - (node.width ?? 0) / 2,
|
||||
y: position.y - (node.height ?? 0) / 2,
|
||||
},
|
||||
x: position.x - (node.width ?? 0) / 2,
|
||||
y: position.y - (node.height ?? 0) / 2,
|
||||
};
|
||||
}),
|
||||
edges,
|
||||
|
||||
@@ -35,14 +35,16 @@ export function Panels({ children, ...props }: PanelsProps) {
|
||||
)}
|
||||
<Panel unstyled position="bottom-right">
|
||||
{props.zoom && (
|
||||
<Panel.Wrapper className="px-1.5">
|
||||
<Panel.IconButton Icon={TbPlus} round onClick={handleZoomIn} />
|
||||
<Panel.Text className="px-2" mono onClick={handleZoomReset}>
|
||||
{percent}%
|
||||
</Panel.Text>
|
||||
<Panel.IconButton Icon={TbMinus} round onClick={handleZoomOut} />
|
||||
<Panel.IconButton Icon={TbMaximize} round onClick={handleZoomReset} />
|
||||
</Panel.Wrapper>
|
||||
<>
|
||||
<Panel.Wrapper className="px-1.5">
|
||||
<Panel.IconButton Icon={TbPlus} round onClick={handleZoomIn} />
|
||||
<Panel.Text className="px-2" mono onClick={handleZoomReset}>
|
||||
{percent}%
|
||||
</Panel.Text>
|
||||
<Panel.IconButton Icon={TbMinus} round onClick={handleZoomOut} />
|
||||
<Panel.IconButton Icon={TbMaximize} round onClick={handleZoomReset} />
|
||||
</Panel.Wrapper>
|
||||
</>
|
||||
)}
|
||||
{props.minimap && (
|
||||
<>
|
||||
|
||||
@@ -4,15 +4,12 @@
|
||||
// there is no lifecycle or Hook in React that we can use to switch
|
||||
// .current at the right timing."
|
||||
// So we will have to make do with this "close enough" approach for now.
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useLayoutEffect, useRef } from "react";
|
||||
import { isDebug } from "core";
|
||||
|
||||
export const useEvent = <Fn>(fn: Fn | ((...args: any[]) => any) | undefined): Fn => {
|
||||
const ref = useRef([fn, (...args) => ref[0](...args)]).current;
|
||||
// Per Dan Abramov: useInsertionEffect executes marginally closer to the
|
||||
// correct timing for ref synchronization than useLayoutEffect on React 18.
|
||||
// See: https://github.com/facebook/react/pull/25881#issuecomment-1356244360
|
||||
useEffect(() => {
|
||||
ref[0] = fn;
|
||||
}, []);
|
||||
return ref[1];
|
||||
export const useEvent = <Fn>(fn: Fn): Fn => {
|
||||
if (isDebug()) {
|
||||
console.warn("useEvent() is deprecated");
|
||||
}
|
||||
return fn;
|
||||
};
|
||||
|
||||
@@ -6,10 +6,6 @@ import { layoutWithDagre } from "ui/components/canvas/layouts";
|
||||
import { Panels } from "ui/components/canvas/panels";
|
||||
import { EntityTableNode } from "./EntityTableNode";
|
||||
import { useTheme } from "ui/client/use-theme";
|
||||
import { Panel } from "ui/components/canvas/panels/Panel";
|
||||
import { TbLayout } from "react-icons/tb";
|
||||
import { useState } from "react";
|
||||
import { type CanvasPosition, dataCanvasStore } from "ui/store";
|
||||
|
||||
function entitiesToNodes(entities: AppDataConfig["entities"]): Node<TAppDataEntity>[] {
|
||||
return Object.entries(entities ?? {}).map(([name, entity]) => {
|
||||
@@ -69,45 +65,12 @@ const nodeTypes = {
|
||||
entity: EntityTableNode.Component,
|
||||
} as const;
|
||||
|
||||
function getNodeAutoLayout(nodes: Node<TAppDataEntity>[], edges: any[]): CanvasPosition[] {
|
||||
const nodeLayout = layoutWithDagre({
|
||||
nodes: nodes.map((n) => ({
|
||||
id: n.id,
|
||||
...EntityTableNode.getSize(n.data),
|
||||
})),
|
||||
edges,
|
||||
graph: {
|
||||
rankdir: "LR",
|
||||
marginx: 50,
|
||||
marginy: 50,
|
||||
},
|
||||
});
|
||||
|
||||
return nodeLayout.nodes.map((n) => ({
|
||||
id: n.id,
|
||||
...n.position,
|
||||
}));
|
||||
}
|
||||
|
||||
function setNodesLayout(nodes: Node<TAppDataEntity>[], layout: CanvasPosition[]) {
|
||||
return nodes.map((node) => {
|
||||
const pos = layout.find((l) => l.id === node.id);
|
||||
if (pos) {
|
||||
return {
|
||||
...node,
|
||||
position: { x: pos.x, y: pos.y },
|
||||
};
|
||||
}
|
||||
return node;
|
||||
});
|
||||
}
|
||||
|
||||
export function DataSchemaCanvas() {
|
||||
const {
|
||||
config: { data },
|
||||
} = useBknd();
|
||||
const { theme } = useTheme();
|
||||
|
||||
const nodes = entitiesToNodes(data.entities);
|
||||
const edges = relationsToEdges(data.relations).map((e) => ({
|
||||
...e,
|
||||
style: {
|
||||
@@ -122,35 +85,31 @@ export function DataSchemaCanvas() {
|
||||
},
|
||||
}));
|
||||
|
||||
const entityNodes = entitiesToNodes(data.entities);
|
||||
const positions = dataCanvasStore((state) => state.positions);
|
||||
const setPositions = dataCanvasStore((state) => state.setPositions);
|
||||
const resetPositions = dataCanvasStore((state) => state.reset);
|
||||
const nodeLayout = layoutWithDagre({
|
||||
nodes: nodes.map((n) => ({
|
||||
id: n.id,
|
||||
...EntityTableNode.getSize(n.data),
|
||||
})),
|
||||
edges,
|
||||
graph: {
|
||||
rankdir: "LR",
|
||||
marginx: 50,
|
||||
marginy: 50,
|
||||
},
|
||||
});
|
||||
|
||||
const layout = positions ? positions : getNodeAutoLayout(entityNodes, edges);
|
||||
const [nodes, setNodes] = useState<Node<TAppDataEntity>[]>(setNodesLayout(entityNodes, layout));
|
||||
|
||||
function setLayout(positions: CanvasPosition[] = getNodeAutoLayout(entityNodes, edges)) {
|
||||
setNodes(setNodesLayout(entityNodes, positions));
|
||||
}
|
||||
|
||||
function resetLayout() {
|
||||
resetPositions();
|
||||
setLayout();
|
||||
}
|
||||
nodeLayout.nodes.forEach((node) => {
|
||||
const n = nodes.find((n) => n.id === node.id);
|
||||
if (n) {
|
||||
n.position = { x: node.x, y: node.y };
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<Canvas
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodeDragStop={(e, node) => {
|
||||
const positions = nodes
|
||||
.map((n) => (n.id === node.id ? node : n))
|
||||
.map((n) => ({ id: n.id, ...n.position }));
|
||||
setPositions(positions);
|
||||
setLayout(positions);
|
||||
}}
|
||||
nodeTypes={nodeTypes}
|
||||
minZoom={0.1}
|
||||
maxZoom={2}
|
||||
@@ -159,11 +118,7 @@ export function DataSchemaCanvas() {
|
||||
maxZoom: 0.8,
|
||||
}}
|
||||
>
|
||||
<Panels zoom minimap>
|
||||
<Panel position="bottom-left">
|
||||
<Panel.IconButton round Icon={TbLayout} onClick={resetLayout} />
|
||||
</Panel>
|
||||
</Panels>
|
||||
<Panels zoom minimap />
|
||||
</Canvas>
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
|
||||
@@ -240,8 +240,8 @@ const StrategyPasswordForm = () => {
|
||||
const StrategyOAuthForm = () => {
|
||||
return (
|
||||
<>
|
||||
<Field name="config.client.client_id" required />
|
||||
<Field name="config.client.client_secret" required />
|
||||
<Field name="config.client.client_id" required inputProps={{ type: "password" }} />
|
||||
<Field name="config.client.client_secret" required inputProps={{ type: "password" }} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { create } from "zustand";
|
||||
import { combine, persist } from "zustand/middleware";
|
||||
|
||||
export type CanvasPosition = {
|
||||
id: string;
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export const dataCanvasStore = create(
|
||||
persist(
|
||||
combine(
|
||||
{
|
||||
positions: null as CanvasPosition[] | null,
|
||||
},
|
||||
(set) => ({
|
||||
setPositions: (positions: CanvasPosition[]) => set(() => ({ positions })),
|
||||
reset: () => set(() => ({ positions: null })),
|
||||
}),
|
||||
),
|
||||
{
|
||||
name: "datacanvas",
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -1,2 +1 @@
|
||||
export { appShellStore } from "./appshell";
|
||||
export { dataCanvasStore, type CanvasPosition } from "./datacanvas";
|
||||
|
||||
+37
-3
@@ -2,11 +2,10 @@
|
||||
title: Introduction
|
||||
---
|
||||
|
||||
import { cloudflare, nextjs, reactRouter, astro, bun, node, docker, vite, aws } from "/snippets/integration-icons.mdx"
|
||||
import { cloudflare, nextjs, reactRouter, astro, bun, node, docker, vite, aws, d1, libsql, sqlite, postgres, turso } from "/snippets/integration-icons.mdx"
|
||||
import { Stackblitz, examples } from "/snippets/stackblitz.mdx"
|
||||
|
||||
Glad you're here! This is about **bknd**, a feature-rich backend that is so lightweight it could
|
||||
run on your toaster (probably).
|
||||
Glad you're here! **bknd** is a lightweight, infrastructure agnostic and feature-rich backend that runs in any JavaScript environment.
|
||||
|
||||
## Preview
|
||||
Here is a preview of **bknd** in StackBlitz:
|
||||
@@ -91,3 +90,38 @@ in the future, so stay tuned!
|
||||
Create a new issue to request a guide for your runtime or framework.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Use your favorite SQL Database
|
||||
The following databases are currently supported. Request a new integration if your favorite is missing.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="LibSQL/SQLite"
|
||||
icon={<div className="text-primary-light">{libsql}</div>}
|
||||
href="/usage/database#database"
|
||||
/>
|
||||
<Card
|
||||
title="Turso"
|
||||
icon={<div className="text-primary-light">{turso}</div>}
|
||||
href="/usage/database#sqlite-using-libsql-on-turso"
|
||||
/>
|
||||
<Card
|
||||
title="PostgreSQL"
|
||||
icon={<div className="text-primary-light">{postgres}</div>}
|
||||
href="/usage/database#postgresql"
|
||||
/>
|
||||
<Card
|
||||
title="Cloudflare D1"
|
||||
icon={<div className="text-primary-light">{d1}</div>}
|
||||
href="/usage/database#cloudflare-d1"
|
||||
/>
|
||||
<div style={{ gridColumn: "span 2" }}>
|
||||
<Card
|
||||
horizontal
|
||||
title="Yours missing?"
|
||||
href="https://github.com/bknd-io/bknd/issues/new"
|
||||
>
|
||||
Create a new issue to request a new database integration.
|
||||
</Card>
|
||||
</div>
|
||||
</CardGroup>
|
||||
|
||||
@@ -27,6 +27,33 @@ export const vite = <svg xmlns="http://www.w3.org/2000/svg" width="28" height="2
|
||||
</svg>
|
||||
|
||||
export const docker = <svg xmlns="http://www.w3.org/2000/svg" width={30} height={30} viewBox="0 0 24 24"><path
|
||||
fill="currentColor" d="M21.81 10.25c-.06-.04-.56-.43-1.64-.43c-.28 0-.56.03-.84.08c-.21-1.4-1.38-2.11-1.43-2.14l-.29-.17l-.18.27c-.24.36-.43.77-.51 1.19c-.2.8-.08 1.56.33 2.21c-.49.28-1.29.35-1.46.35H2.62c-.34 0-.62.28-.62.63c0 1.15.18 2.3.58 3.38c.45 1.19 1.13 2.07 2 2.61c.98.6 2.59.94 4.42.94c.79 0 1.61-.07 2.42-.22c1.12-.2 2.2-.59 3.19-1.16A8.3 8.3 0 0 0 16.78 16c1.05-1.17 1.67-2.5 2.12-3.65h.19c1.14 0 1.85-.46 2.24-.85c.26-.24.45-.53.59-.87l.08-.24zm-17.96.99h1.76c.08 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16-.16H3.85c-.09 0-.16.07-.16.16v1.58c.01.09.07.16.16.16m2.43 0h1.76c.08 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16-.16H6.28c-.09 0-.16.07-.16.16v1.58c.01.09.07.16.16.16m2.47 0h1.75c.1 0 .17-.07.17-.16V9.5c0-.08-.06-.16-.17-.16H8.75c-.08 0-.15.07-.15.16v1.58c0 .09.06.16.15.16m2.44 0h1.77c.08 0 .15-.07.15-.16V9.5c0-.08-.06-.16-.15-.16h-1.77c-.08 0-.15.07-.15.16v1.58c0 .09.07.16.15.16M6.28 9h1.76c.08 0 .16-.09.16-.18V7.25c0-.09-.07-.16-.16-.16H6.28c-.09 0-.16.06-.16.16v1.57c.01.09.07.18.16.18m2.47 0h1.75c.1 0 .17-.09.17-.18V7.25c0-.09-.06-.16-.17-.16H8.75c-.08 0-.15.06-.15.16v1.57c0 .09.06.18.15.18m2.44 0h1.77c.08 0 .15-.09.15-.18V7.25c0-.09-.07-.16-.15-.16h-1.77c-.08 0-.15.06-.15.16v1.57c0 .09.07.18.15.18m0-2.28h1.77c.08 0 .15-.07.15-.16V5c0-.1-.07-.17-.15-.17h-1.77c-.08 0-.15.06-.15.17v1.56c0 .08.07.16.15.16m2.46 4.52h1.76c.09 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16-.16h-1.76c-.08 0-.15.07-.15.16v1.58c0 .09.07.16.15.16"></path></svg>
|
||||
fill="currentColor" d="M21.81 10.25c-.06-.04-.56-.43-1.64-.43c-.28 0-.56.03-.84.08c-.21-1.4-1.38-2.11-1.43-2.14l-.29-.17l-.18.27c-.24.36-.43.77-.51 1.19c-.2.8-.08 1.56.33 2.21c-.49.28-1.29.35-1.46.35H2.62c-.34 0-.62.28-.62.63c0 1.15.18 2.3.58 3.38c.45 1.19 1.13 2.07 2 2.61c.98.6 2.59.94 4.42.94c.79 0 1.61-.07 2.42-.22c1.12-.2 2.2-.59 3.19-1.16A8.3 8.3 0 0 0 16.78 16c1.05-1.17 1.67-2.5 2.12-3.65h.19c1.14 0 1.85-.46 2.24-.85c.26-.24.45-.53.59-.87l.08-.24zm-17.96.99h1.76c.08 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16-.16H3.85c-.09 0-.16.07-.16.16v1.58c.01.09.07.16.16.16m2.43 0h1.76c.08 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16H6.28c-.09 0-.16.07-.16.16v1.58c.01.09.07.16.16.16m2.47 0h1.75c.1 0 .17-.07.17-.16V9.5c0-.08-.06-.16-.17-.16H8.75c-.08 0-.15.07-.15.16v1.58c0 .09.06.16.15.16m2.44 0h1.77c.08 0 .15-.07.15-.16V9.5c0-.08-.06-.16-.15-.16h-1.77c-.08 0-.15.07-.15.16v1.58c0 .09.07.16.15.16M6.28 9h1.76c.08 0 .16-.09.16-.18V7.25c0-.09-.07-.16-.16-.16H6.28c-.09 0-.16.06-.16.16v1.57c.01.09.07.18.16.18m2.47 0h1.75c.1 0 .17-.09.17-.18V7.25c0-.09-.06-.16-.17-.16H8.75c-.08 0-.15.06-.15.16v1.57c0 .09.06.18.15.18m2.44 0h1.77c.08 0 .15-.09.15-.18V7.25c0-.09-.07-.16-.15-.16h-1.77c-.08 0-.15.06-.15.16v1.57c0 .09.07.18.15.18m0-2.28h1.77c.08 0 .15-.07.15-.16V5c0-.1-.07-.17-.15-.17h-1.77c-.08 0-.15.06-.15.17v1.56c0 .08.07.16.15.16m2.46 4.52h1.76c.09 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16-.16h-1.76c-.08 0-.15.07-.15.16v1.58c0 .09.07.16.15.16"></path></svg>
|
||||
|
||||
export const aws = <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M4.986 0a.545.545 0 0 0-.534.548l-.006 4.908c0 .145.06.283.159.39a.53.53 0 0 0 .38.155h3.429l8.197 17.68a.54.54 0 0 0 .488.319h5.811a.547.547 0 0 0 .543-.548v-4.908a.543.543 0 0 0-.543-.548h-2.013L12.739.316A.55.55 0 0 0 12.245 0H4.991Zm.54 1.09h6.367l8.16 17.681a.54.54 0 0 0 .489.318h1.817v3.817h-4.922L9.24 5.226a.54.54 0 0 0-.488-.318h-3.23Zm2.013 8.237a.54.54 0 0 0-.486.31L.6 23.213a.55.55 0 0 0 .032.528a.53.53 0 0 0 .454.25h6.169a.55.55 0 0 0 .497-.31l3.38-7.165a.54.54 0 0 0-.003-.469l-3.093-6.41a.55.55 0 0 0-.494-.31Zm.006 1.804l2.488 5.152l-3.122 6.62H1.947Z" stroke-width="0.5" stroke="currentColor"/></svg>
|
||||
export const aws = <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M4.986 0a.545.545 0 0 0-.534.548l-.006 4.908c0 .145.06.283.159.39a.53.53 0 0 0 .38.155h3.429l8.197 17.68a.54.54 0 0 0 .488.319h5.811a.547.547 0 0 0 .543-.548v-4.908a.543.543 0 0 0-.543-.548h-2.013L12.739.316A.55.55 0 0 0 12.245 0H4.991Zm.54 1.09h6.367l8.16 17.681a.54.54 0 0 0 .489.318h1.817v3.817h-4.922L9.24 5.226a.54.54 0 0 0-.488-.318h-3.23Zm2.013 8.237a.54.54 0 0 0-.486.31L.6 23.213a.55.55 0 0 0 .032.528a.53.53 0 0 0 .454.25h6.169a.55.55 0 0 0 .497-.31l3.38-7.165a.54.54 0 0 0-.003-.469l-3.093-6.41a.55.55 0 0 0-.494-.31Zm.006 1.804l2.488 5.152l-3.122 6.62H1.947Z" stroke-width="0.5" stroke="currentColor"/></svg>
|
||||
|
||||
export const postgres = <svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24"><path fill="currentColor" d="M23.56 14.723a.5.5 0 0 0-.057-.12q-.21-.395-1.007-.231c-1.654.34-2.294.13-2.526-.02c1.342-2.048 2.445-4.522 3.041-6.83c.272-1.05.798-3.523.122-4.73a1.6 1.6 0 0 0-.15-.236C21.693.91 19.8.025 17.51.001c-1.495-.016-2.77.346-3.116.479a10 10 0 0 0-.516-.082a8 8 0 0 0-1.312-.127c-1.182-.019-2.203.264-3.05.84C8.66.79 4.729-.534 2.296 1.19C.935 2.153.309 3.873.43 6.304c.041.818.507 3.334 1.243 5.744q.69 2.26 1.433 3.582q.83 1.493 1.714 1.79c.448.148 1.133.143 1.858-.729a56 56 0 0 1 1.945-2.206c.435.235.906.362 1.39.377v.004a11 11 0 0 0-.247.305c-.339.43-.41.52-1.5.745c-.31.064-1.134.233-1.146.811a.6.6 0 0 0 .091.327c.227.423.922.61 1.015.633c1.335.333 2.505.092 3.372-.679c-.017 2.231.077 4.418.345 5.088c.221.553.762 1.904 2.47 1.904q.375.001.829-.094c1.782-.382 2.556-1.17 2.855-2.906c.15-.87.402-2.875.539-4.101c.017-.07.036-.12.057-.136c0 0 .07-.048.427.03l.044.007l.254.022l.015.001c.847.039 1.911-.142 2.531-.43c.644-.3 1.806-1.033 1.595-1.67M2.37 11.876c-.744-2.435-1.178-4.885-1.212-5.571c-.109-2.172.417-3.683 1.562-4.493c1.837-1.299 4.84-.54 6.108-.13l-.01.01C6.795 3.734 6.843 7.226 6.85 7.44c0 .082.006.199.016.36c.034.586.1 1.68-.074 2.918c-.16 1.15.194 2.276.973 3.089q.12.126.252.237c-.347.371-1.1 1.193-1.903 2.158c-.568.682-.96.551-1.088.508c-.392-.13-.813-.587-1.239-1.322c-.48-.839-.963-2.032-1.415-3.512m6.007 5.088a1.6 1.6 0 0 1-.432-.178c.089-.039.237-.09.483-.14c1.284-.265 1.482-.451 1.915-1a8 8 0 0 1 .367-.443a.4.4 0 0 0 .074-.13c.17-.151.272-.11.436-.042c.156.065.308.26.37.475c.03.102.062.295-.045.445c-.904 1.266-2.222 1.25-3.168 1.013m2.094-3.988l-.052.14c-.133.357-.257.689-.334 1.004c-.667-.002-1.317-.288-1.81-.803c-.628-.655-.913-1.566-.783-2.5c.183-1.308.116-2.447.08-3.059l-.013-.22c.296-.262 1.666-.996 2.643-.772c.446.102.718.406.83.928c.585 2.704.078 3.83-.33 4.736a9 9 0 0 0-.23.546m7.364 4.572q-.024.266-.062.596l-.146.438a.4.4 0 0 0-.018.108c-.006.475-.054.649-.115.87a4.8 4.8 0 0 0-.18 1.057c-.11 1.414-.878 2.227-2.417 2.556c-1.515.325-1.784-.496-2.02-1.221a7 7 0 0 0-.078-.227c-.215-.586-.19-1.412-.157-2.555c.016-.561-.025-1.901-.33-2.646q.006-.44.019-.892a.4.4 0 0 0-.016-.113a2 2 0 0 0-.044-.208c-.122-.428-.42-.786-.78-.935c-.142-.059-.403-.167-.717-.087c.067-.276.183-.587.309-.925l.053-.142c.06-.16.134-.325.213-.5c.426-.948 1.01-2.246.376-5.178c-.237-1.098-1.03-1.634-2.232-1.51c-.72.075-1.38.366-1.709.532a6 6 0 0 0-.196.104c.092-1.106.439-3.174 1.736-4.482a4 4 0 0 1 .303-.276a.35.35 0 0 0 .145-.064c.752-.57 1.695-.85 2.802-.833q.616.01 1.174.081c1.94.355 3.244 1.447 4.036 2.383c.814.962 1.255 1.931 1.431 2.454c-1.323-.134-2.223.127-2.68.78c-.992 1.418.544 4.172 1.282 5.496c.135.242.252.452.289.54c.24.583.551.972.778 1.256c.07.087.138.171.189.245c-.4.116-1.12.383-1.055 1.717a35 35 0 0 1-.084.815c-.046.208-.07.46-.1.766m.89-1.621c-.04-.832.27-.919.597-1.01l.135-.041a1 1 0 0 0 .134.103c.57.376 1.583.421 3.007.134c-.202.177-.519.4-.953.601c-.41.19-1.096.333-1.747.364c-.72.034-1.086-.08-1.173-.151m.57-9.271a7 7 0 0 1-.105 1.001c-.055.358-.112.728-.127 1.177c-.014.436.04.89.093 1.33c.107.887.216 1.8-.207 2.701a4 4 0 0 1-.188-.385a8 8 0 0 0-.325-.617c-.616-1.104-2.057-3.69-1.32-4.744c.38-.543 1.342-.566 2.179-.463m.228 7.013l-.085-.107l-.035-.044c.726-1.2.584-2.387.457-3.439c-.052-.432-.1-.84-.088-1.222c.013-.407.066-.755.118-1.092c.064-.415.13-.844.111-1.35a.6.6 0 0 0 .012-.19c-.046-.486-.6-1.938-1.73-3.253a7.8 7.8 0 0 0-2.688-2.04A9.3 9.3 0 0 1 17.62.746c2.052.046 3.675.814 4.824 2.283a1 1 0 0 1 .067.1c.723 1.356-.276 6.275-2.987 10.54m-8.816-6.116c-.025.18-.31.423-.621.423l-.081-.006a.8.8 0 0 1-.506-.315c-.046-.06-.12-.178-.106-.285a.22.22 0 0 1 .093-.149c.118-.089.352-.122.61-.086c.316.044.642.193.61.418m7.93-.411c.011.08-.049.2-.153.31a.72.72 0 0 1-.408.223l-.075.005c-.293 0-.541-.234-.56-.371c-.024-.177.264-.31.56-.352c.298-.042.612.009.636.185" stroke-width="1" stroke="currentColor"/></svg>;
|
||||
|
||||
export const libsql = <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 217.2 217.2">
|
||||
<path fill="#72f5cf" d="M118,87.2c.7,0,1.3,0,1.9.3.4.1.8.3,1.2.5.2.1.4.2.6.3.4.3.7.5,1.1.9l5.2,5.2c2.7,2.7,2.7,7,0,9.7l-95.1,95.1c-1.3,1.3-3.1,2-4.8,2s-3.5-.7-4.8-2l-5.2-5.2c-2.7-2.7-2.7-7,0-9.7l85.1-85.1,10-10c.3-.3.7-.6,1.1-.9.2-.1.4-.2.6-.3.4-.2.8-.4,1.2-.5.6-.2,1.3-.3,1.9-.3M118,71.2c-2.2,0-4.4.3-6.5.9-1.4.4-2.8,1-4.1,1.7-.7.4-1.3.7-2,1.2-1.3.8-2.5,1.8-3.6,2.9l-10,10L6.7,173c-8.9,8.9-8.9,23.4,0,32.3l5.2,5.2c4.3,4.3,10.1,6.7,16.2,6.7s11.8-2.4,16.2-6.7l95.1-95.1c4.3-4.3,6.7-10.1,6.7-16.2s-2.4-11.8-6.7-16.2l-5.2-5.2c-1.1-1.1-2.3-2.1-3.6-2.9-.6-.4-1.3-.8-1.9-1.2-1.3-.7-2.7-1.3-4.1-1.7-2.1-.6-4.3-.9-6.5-.9h0Z"/>
|
||||
<g>
|
||||
<path fill="#72f5cf" d="M119.4,16c.3,0,.6,0,.9,0l66.5,3.8c5.8.3,10.3,4.9,10.7,10.7l3.8,66.5c.3,4.4-1.4,8.7-4.5,11.8l-79.7,79.7c-3,3-6.9,4.5-11,4.5s-3.3-.3-4.9-.8l-49.9-16.5c-4.7-1.5-8.3-5.2-9.8-9.8l-16.5-49.9c-1.8-5.6-.4-11.7,3.8-15.8L108.4,20.5c2.9-2.9,6.9-4.5,11-4.5M119.4,0c-8.4,0-16.3,3.3-22.3,9.2L17.4,88.9c-8.5,8.5-11.4,20.8-7.6,32.1l16.5,49.9c3.1,9.4,10.6,16.9,20,20l49.9,16.5c3.2,1.1,6.5,1.6,9.9,1.6,8.4,0,16.3-3.3,22.3-9.2l79.7-79.7c6.3-6.3,9.7-15.1,9.2-24.1l-3.8-66.5c-.8-13.9-11.9-24.9-25.7-25.7L121.2,0c-.6,0-1.2,0-1.8,0h0Z"/>
|
||||
<path fill="#a8f7d9" d="M24.9,116.1l16.5,49.9c1.5,4.7,5.2,8.3,9.8,9.8l49.9,16.5c5.6,1.8,11.7.4,15.8-3.8l79.7-79.7c3.1-3.1,4.8-7.4,4.5-11.8l-3.8-66.5c-.3-5.8-4.9-10.3-10.7-10.7l-66.5-3.8c-4.4-.3-8.7,1.4-11.8,4.5L28.7,100.3c-4.1,4.1-5.6,10.3-3.8,15.8Z"/>
|
||||
<path fill="#141b1f" d="M119.4,16c.3,0,.6,0,.9,0l66.5,3.8c5.8.3,10.3,4.9,10.7,10.7l3.8,66.5c.3,4.4-1.4,8.7-4.5,11.8l-79.7,79.7c-3,3-6.9,4.5-11,4.5s-3.3-.3-4.9-.8l-49.9-16.5c-4.7-1.5-8.3-5.2-9.8-9.8l-16.5-49.9c-1.8-5.6-.4-11.7,3.8-15.8L108.4,20.5c2.9-2.9,6.9-4.5,11-4.5M119.4,6c-6.8,0-13.2,2.7-18,7.5L21.6,93.2c-6.8,6.8-9.2,16.8-6.2,26l16.5,49.9c2.5,7.6,8.6,13.7,16.2,16.2l49.9,16.5c2.6.9,5.3,1.3,8,1.3,6.8,0,13.2-2.7,18-7.5l79.7-79.7c5.1-5.1,7.8-12.2,7.4-19.5l-3.8-66.5c-.6-10.8-9.3-19.5-20.1-20.1l-66.5-3.8c-.5,0-1,0-1.5,0h0Z"/>
|
||||
<path fill="#141b1f" d="M136.7,173.7l6.9-6.9c-.2-.1-.4-.2-.6-.3l-27.6-9.1-6.9,6.9,28.3,9.3Z"/>
|
||||
<path fill="#141b1f" d="M166.5,143.9l6.9-6.9c-.2-.1-.4-.2-.6-.3l-27.6-9.1-6.9,6.9,28.3,9.3Z"/>
|
||||
<path fill="#141b1f" d="M43.5,80.5l6.9-6.9c.1.2.2.4.3.6l9.1,27.6-6.9,6.9-9.3-28.3Z"/>
|
||||
<path fill="#141b1f" d="M73.3,50.7l6.9-6.9c.1.2.2.4.3.6l9.1,27.6-6.9,6.9-9.3-28.3Z"/>
|
||||
</g>
|
||||
<path fill="#79ac91" d="M130.6,101.5l-97.7,97.7c-2.7,2.7-7,2.7-9.7,0l-5.2-5.2c-2.7-2.7-2.7-7,0-9.7l97.7-97.7c1.5-1.5,3.4-2.4,5.5-2.6l7.9-.8c2.8-.3,5.1,2.1,4.9,4.9l-.8,7.9c-.2,2.1-1.1,4-2.6,5.5Z"/>
|
||||
<path fill="#141b1f" d="M129.5,83.3c2.6,0,4.7,2.2,4.4,4.9l-.8,7.9c-.2,2.1-1.1,4-2.6,5.5l-97.7,97.7c-1.3,1.3-3.1,2-4.8,2s-3.5-.7-4.8-2l-5.2-5.2c-2.7-2.7-2.7-7,0-9.7l97.7-97.7c1.5-1.5,3.4-2.4,5.5-2.6l7.9-.8c.1,0,.3,0,.4,0M129.5,73.3h0c-.5,0-.9,0-1.4,0l-7.9.8c-4.4.4-8.5,2.4-11.5,5.5L10.9,177.3c-6.6,6.6-6.6,17.3,0,23.8l5.2,5.2c3.2,3.2,7.4,4.9,11.9,4.9s8.7-1.8,11.9-4.9l97.7-97.7c3.1-3.1,5-7.2,5.5-11.5l.8-7.9c.4-4-.9-8.1-3.7-11.1-2.7-3-6.6-4.7-10.7-4.7h0Z"/>
|
||||
</svg>;
|
||||
|
||||
export const sqlite = <svg xmlns="http://www.w3.org/2000/svg" width="28px" height="28px" viewBox="0 0 128 128"><path fill="currentColor" d="M115.95 2.781c-5.504-4.906-12.16-2.933-18.738 2.902a48 48 0 0 0-2.918 2.856c-11.246 11.93-21.684 34.02-24.926 50.895c1.262 2.563 2.25 5.832 2.902 8.328a71 71 0 0 1 .875 3.746s-.101-.379-.515-1.578l-.266-.777a8 8 0 0 0-.176-.426c-.734-1.707-2.761-5.309-3.656-6.875a172 172 0 0 0-2.008 6.27c2.582 4.714 4.149 12.8 4.149 12.8s-.133-.527-.782-2.355c-.57-1.617-3.437-6.637-4.117-7.809c-1.16 4.29-1.62 7.18-1.207 7.883c.813 1.363 1.578 3.723 2.25 6.324c1.528 5.868 2.586 13.016 2.586 13.016l.094 1.192c-.203 4.886-.102 9.781.297 14.656c.508 6.113 1.457 11.359 2.668 14.172l.824-.45c-1.781-5.535-2.504-12.792-2.184-21.155c.477-12.79 3.422-28.215 8.856-44.29c9.191-24.261 21.938-43.733 33.602-53.034c-10.63 9.601-25.023 40.695-29.332 52.203C79.404 74.162 75.99 86.252 73.93 97.84c3.555-10.863 15.043-15.527 15.043-15.527s5.637-6.954 12.223-16.883c-3.945.898-10.426 2.441-12.598 3.351c-3.2 1.34-4.063 1.797-4.063 1.797s10.371-6.312 19.27-9.172c12.234-19.27 25.566-46.645 12.145-58.625m-99.054 2.9c-5.398.02-9.77 4.39-9.785 9.789v88.574c.016 5.398 4.39 9.765 9.785 9.785h50.227a123 123 0 0 1-.277-14.438c-.031-.332-.059-.754-.086-1.067a143 143 0 0 0-2.523-12.684c-.645-2.507-1.465-4.789-1.965-5.636c-.621-1.051-.524-1.653-.52-2.305c0-.64.082-1.305.2-2.059q.473-2.818 1.246-5.574l1.156-.148c-.09-.188-.074-.348-.164-.516l-.219-2.031A169 169 0 0 1 66.011 61l1.066-.102c-.043-.082-.055-.203-.098-.28l-.23-1.685c3.363-17.496 13.8-39.699 25.601-52.219c.352-.37.711-.683 1.055-1.035z"/></svg>;
|
||||
export const sqlocal = 0;
|
||||
|
||||
export const turso =
|
||||
<svg width="36" height="36" viewBox="0 0 241 240" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M220.035 83.61C215.365 55.67 190.875 35 190.875 35V65.78L176.335 69.53L167.225 58.56L162.415 68.02C152.495 65.32 138.835 63.58 120.045 63.58C101.255 63.58 87.595 65.33 77.675 68.02L72.865 58.56L63.7549 69.53L49.2149 65.78V35C49.2149 35 24.7249 55.67 20.0549 83.61L52.1949 94.73C53.2449 114.16 61.9849 166.61 64.4849 171.37C67.1449 176.44 81.2649 190.93 92.3149 196.5C92.3149 196.5 96.3149 192.27 98.7549 188.54C101.855 192.19 117.865 204.99 120.055 204.99C122.245 204.99 138.255 192.2 141.355 188.54C143.795 192.27 147.795 196.5 147.795 196.5C158.845 190.93 172.965 176.44 175.625 171.37C178.125 166.61 186.865 114.16 187.915 94.73L220.055 83.61H220.035ZM173.845 128.35L152.095 130.29L154.005 156.96C154.005 156.96 140.775 167.91 120.045 167.91C99.315 167.91 86.0849 156.96 86.0849 156.96L87.995 130.29L66.2449 128.35L62.5249 98.31L98.5749 110.79L95.7749 148.18C102.475 149.88 109.525 151.57 120.055 151.57C130.585 151.57 137.625 149.88 144.325 148.18L141.525 110.79L177.575 98.31L173.855 128.35H173.845Z" fill="currentColor"/>
|
||||
</svg>;
|
||||
|
||||
export const d1 = <svg width="30" height="30"> <symbol id="ai:local:d1" viewBox="0 0 65 64"><path fill="currentColor" d="m23.6 22.2 3.03 1.75v3.5L23.6 29.2l-3.03-1.75v-3.5zM20.06 49l3.54-3.54L27.14 49l-3.54 3.54zm3.54-14.7c.593 0 1.17.176 1.67.506.493.33.878.798 1.1 1.35a3 3 0 0 1-.65 3.27c-.42.42-.954.705-1.54.821a3 3 0 0 1-1.73-.171 3.04 3.04 0 0 1-1.35-1.1 3 3 0 0 1-.506-1.67c0-.796.316-1.56.879-2.12a3 3 0 0 1 2.12-.879zM10.3 11.2l6.42-4.89 1.21-.37h29l1.19.39 6.61 4.89.82 1.61v38L55 52.21l-4.83 5.11-1.46.63h-31.7l-1.37-.54-5.48-5.11-.64-1.47v-38zm3.21 25.4 4.47 4.94h.056v4h-1.83l-2.7-3v7.39l4.26 4h30l3.7-3.91V42.3l-3.67 3.24h-18.6v-4h17.2l5.19-4.61v-7.44l-3.67 3.25h-18.7v-4h17.2l5.19-4.6v-6.92l-3.67 3.26h-31.6l-2.74-2.8v6.12l4.47 4.94h.056v4h-1.83l-2.7-3zm32.7-26.7h-27.6l-4.07 3.11 3.4 3.48h28.4l4-3.56z"></path></symbol><use href="#ai:local:d1"></use> </svg>;
|
||||
+58
-5
@@ -55,12 +55,65 @@ connection object to your new database:
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Connection
|
||||
<Note>
|
||||
Follow the progress of custom connections on its [Github Issue](https://github.com/bknd-io/bknd/issues/24).
|
||||
If you're interested, make sure to upvote so it can be prioritized.
|
||||
</Note>
|
||||
### 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:
|
||||
|
||||
```ts
|
||||
import { serve, d1 } from "bknd/adapter/cloudflare";
|
||||
|
||||
export default serve<Env>({
|
||||
app: ({ env }) => d1({ binding: env.D1_BINDING })
|
||||
});
|
||||
```
|
||||
|
||||
### PostgreSQL
|
||||
To use bknd with Postgres, you need to install the `@bknd/postgres` package. You can do so by running the following command:
|
||||
|
||||
```bash
|
||||
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).
|
||||
|
||||
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):
|
||||
|
||||
```js
|
||||
import { serve } from "bknd/adapter/node";
|
||||
import { PostgresConnection } from "@bknd/postgres";
|
||||
|
||||
/** @type {import("bknd/adapter/node").NodeBkndConfig} */
|
||||
const config = {
|
||||
connection: new PostgresConnection({
|
||||
connectionString:
|
||||
"postgresql://user:password@localhost:5432/database",
|
||||
}),
|
||||
};
|
||||
|
||||
serve(config);
|
||||
```
|
||||
|
||||
### 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:
|
||||
|
||||
```bash
|
||||
npm install @bknd/sqlocal
|
||||
```
|
||||
|
||||
This package uses `sqlocal` under the hood. Consult the [sqlocal documentation](https://sqlocal.dallashoffman.com/guide/setup) for connection options:
|
||||
|
||||
```js
|
||||
import { createApp } from "bknd";
|
||||
import { SQLocalConnection } from "@bknd/sqlocal";
|
||||
|
||||
const app = createApp({
|
||||
connection: new SQLocalConnection({
|
||||
databasePath: ":localStorage:",
|
||||
verbose: true,
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
### Custom Connection
|
||||
Any bknd app instantiation accepts as connection either `undefined`, a connection object like
|
||||
described above, or an class instance that extends from `Connection`:
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20240620.0",
|
||||
"typescript": "^5.5.3",
|
||||
"wrangler": "^3.108.1"
|
||||
"wrangler": "^4.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user