updated README and documentation (first part) to match 0.6

This commit is contained in:
dswbx
2025-01-21 18:30:01 +01:00
parent 24542f30cb
commit f64e5dac03
21 changed files with 484 additions and 227 deletions

138
README.md
View File

@@ -1,13 +1,9 @@
[![npm version](https://img.shields.io/npm/v/bknd.svg)](https://npmjs.org/package/bknd
"View this project on NPM")
[![npm version](https://img.shields.io/npm/v/bknd.svg)](https://npmjs.org/package/bknd)
[![npm downloads](https://img.shields.io/npm/dm/bknd)](https://www.npmjs.com/package/bknd)
![bknd](https://raw.githubusercontent.com/bknd-io/bknd/refs/heads/main/docs/_assets/poster.png)
bknd simplifies app development by providing fully functional backend for data management,
authentication, workflows and media. Since it's lightweight and built on Web Standards, it can
be deployed nearly anywhere, including running inside your framework of choice. No more
deploying multiple separate services!
bknd simplifies app development by providing fully functional backend for database management, authentication, media and workflows. Being lightweight and built on Web Standards, it can be deployed nearly anywhere, including running inside your framework of choice. No more deploying multiple separate services!
**For documentation and examples, please visit https://docs.bknd.io.**
@@ -15,33 +11,115 @@ deploying multiple separate services!
> Please keep in mind that **bknd** is still under active development
> and therefore full backward compatibility is not guaranteed before reaching v1.0.0.
## ✨ Features
**📊 Data**: Define, query, and control your data with ease.
- Define entities with fields and relationships, synced directly to your database.
- Supported field types: `primary`, `text`, `number`, `date`, `boolean`, `enum`, `json`, `jsonschema`.
- Relationship types: `one-to-one`, `many-to-one`, `many-to-many`, and `polymorphic`.
- Advanced querying with the **Repository**: filtering, sorting, pagination, and relational data handling.
- Seamlessly manage data with mutators and a robust event system.
- Extend database functionality with batching, introspection, and support for multiple SQL dialects.
## Size
![gzipped size of bknd](https://img.badgesize.io/https://unpkg.com/bknd@0.6.1/dist/index.js?compression=gzip&label=bknd)
![gzipped size of bknd/client](https://img.badgesize.io/https://unpkg.com/bknd@0.6.1/dist/ui/client/index.js?compression=gzip&label=bknd/client)
![gzipped size of bknd/elements](https://img.badgesize.io/https://unpkg.com/bknd@0.6.1/dist/ui/elements/index.js?compression=gzip&label=bknd/elements)
![gzipped size of bknd/ui](https://img.badgesize.io/https://unpkg.com/bknd@0.6.1/dist/ui/index.js?compression=gzip&label=bknd/ui)
**🔐 Auth**: Easily implement reliable authentication strategies.
- Built-in `user` entity with customizable fields.
- Supports multiple authentication strategies:
- Email/password (with hashed storage).
- OAuth/OIDC (Google, GitHub, and more).
- Secure JWT generation and session management.
The unpacked size on npm is misleading, as the `bknd` package includes the backend, the ui components as well as the whole backend bundled into the cli including static assets.
**🖼️ Media**: Effortlessly manage and serve all your media files.
- Upload files with ease.
- Adapter-based support for S3, S3-compatible storage (e.g., R2, Tigris), and Cloudinary.
## Motivation
Creating digital products always requires developing both the backend (the logic) and the frontend (the appearance). Building a backend from scratch demands deep knowledge in areas such as authentication and database management. Using a backend framework can speed up initial development, but it still requires ongoing effort to work within its constraints (e.g., *"how to do X with Y?"*), which can quickly slow you down. Choosing a backend system is a tough decision, as you might not be aware of its limitations until you encounter them.
**🔄 Flows** (UI integration coming soon!): Design and run workflows with seamless automation.
- Create and run workflows with trigger-based automation:
- Manual triggers or events from data, auth, media, or server actions.
- HTTP triggers for external integrations.
- Define tasks in sequence, parallel, or loops, with conditional execution.
- Use reusable sub-workflows to organize complex processes.
- Leverage OpenAPI specifications for API-based tasks.
**The solution:** A backend system that only assumes and implements primitive details, integrates into multiple environments, and adheres to industry standards.
## Features
* ⚡ Instant backend with full REST API:
* **Data**: Define, query, and control your data with ease.
* **Auth**: Easily implement reliable authentication strategies.
* **Media**: Effortlessly manage and serve all your media files.
* **Flows**: Design and run workflows with seamless automation. (UI integration coming soon!)
* 🌐 Built on Web Standards for maximum compatibility
* 🏃‍♂️ Multiple run modes
* standalone using the CLI
* using a JavaScript runtime (Node, Bun, workerd)
* using a React framework (Astro, Remix, Next.js)
* 📦 Official API and React SDK with type-safety
* ⚛️ React elements for auto-configured authentication and media components
## Structure
The package is mainly split into 4 parts, each serving a specific purpose:
| Import | Purpose |
|-----------------------------|------------------------------------------------------|
| `bknd`<br/>`bknd/adapter/*` | Backend including all APIs |
| `bknd/ui` | Admin UI components for react frameworks at |
| `bknd/client` | TypeScript SDK and React hooks for the API endpoints |
| `bknd/elements` | React components for authentication and media |
### The backend (`bknd`)
Serve the backend as an API for any JS runtime or framework. The latter is especially handy, as it allows you to deploy your frontend and backend bundled together. Furthermore it allows adding additional logic in a way you're already familar with. Just add another route and you're good to go.
Here is an example of serving the API using node:
```js index.js
import { serve } from "bknd/adapter/node"
serve();
```
### Integrated admin UI (`bknd/ui`)
The admin UI allows to manage your data including full configuration of your backend using a graphical user interface. Using `vite`, your admin route looks like this:
```tsx
import { Admin } from "bknd/ui"
import "bknd/dist/styles.css";
export default function AdminPage() {
return <Admin />
}
```
### Using the REST API or TypeScript SDK (`bknd/client`)
If you're not using a JavaScript environment, you can still access any endpoint using the REST API:
```bash
curl -XGET <your-endpoint>/api/data/<entity>
{
"data": [
{ "id": 1, ... },
{ "id": 2, ... }
],
"meta": { /* ... */ }
}
```
In a JavaScript environment, you can use the TypeScript SDK with type-safety. The above example would look like this:
```ts
import { Api } from "bknd/client";
const api = new Api({ host: "<endpoint>" });
const { data } = await api.data.readMany("<entity>");
```
If you're using React, there are 2 hooks exposed (`useApi`, `useEntity`), as well as an `swr` wrapper around each (`useApiQuery`, `useEntityQuery`). The `swr` wrapped hooks automatically handled query invalidation:
```tsx
import { useState } from "react";
import { useEntityQuery } from "bknd/client";
export default function App() {
const { data } = useEntityQuery("todos");
return <ul>
{data?.map(todo => (
<li key={todo.id}>{todo.name}</li>
))}
</ul>
}
```
### React elements (`bknd/elements`)
You don't have to figure out API details to include media uploads to your app. For an user avatar upload, this is all you need:
```tsx
import { Media } from "bknd/elements"
export function UserAvatar() {
return <Media.Dropzone
entity={{ name: "users", id: 1, field: "avatar" }}
maxItems={1}
overwrite
/>
}
```
The import path also exports components for login and registration forms which are automatically pointed to the `bknd` defaults.
## 🚀 Quick start

View File

@@ -1,106 +0,0 @@
---
title: 'Development'
description: 'Preview changes locally to update your docs'
---
<Info>
**Prerequisite**: Please install Node.js (version 19 or higher) before proceeding.
</Info>
Follow these steps to install and run Mintlify on your operating system:
**Step 1**: Install Mintlify:
<CodeGroup>
```bash npm
npm i -g mintlify
```
```bash yarn
yarn global add mintlify
```
</CodeGroup>
**Step 2**: Navigate to the docs directory (where the `mint.json` file is located) and execute the following command:
```bash
mintlify dev
```
A local preview of your documentation will be available at `http://localhost:3000`.
### Custom Ports
By default, Mintlify uses port 3000. You can customize the port Mintlify runs on by using the `--port` flag. To run Mintlify on port 3333, for instance, use this command:
```bash
mintlify dev --port 3333
```
If you attempt to run Mintlify on a port that's already in use, it will use the next available port:
```md
Port 3000 is already in use. Trying 3001 instead.
```
## Mintlify Versions
Please note that each CLI release is associated with a specific version of Mintlify. If your local website doesn't align with the production version, please update the CLI:
<CodeGroup>
```bash npm
npm i -g mintlify@latest
```
```bash yarn
yarn global upgrade mintlify
```
</CodeGroup>
## Validating Links
The CLI can assist with validating reference links made in your documentation. To identify any broken links, use the following command:
```bash
mintlify broken-links
```
## Deployment
<Tip>
Unlimited editors available under the [Pro
Plan](https://mintlify.com/pricing) and above.
</Tip>
If the deployment is successful, you should see the following:
<Frame>
<img src="/images/checks-passed.png" style={{ borderRadius: '0.5rem' }} />
</Frame>
## Code Formatting
We suggest using extensions on your IDE to recognize and format MDX. If you're a VSCode user, consider the [MDX VSCode extension](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx) for syntax highlighting, and [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) for code formatting.
## Troubleshooting
<AccordionGroup>
<Accordion title='Error: Could not load the "sharp" module using the darwin-arm64 runtime'>
This may be due to an outdated version of node. Try the following:
1. Remove the currently-installed version of mintlify: `npm remove -g mintlify`
2. Upgrade to Node v19 or higher.
3. Reinstall mintlify: `npm install -g mintlify`
</Accordion>
<Accordion title="Issue: Encountering an unknown error">
Solution: Go to the root of your device and delete the \~/.mintlify folder. Afterwards, run `mintlify dev` again.
</Accordion>
</AccordionGroup>
Curious about what changed in the CLI version? [Check out the CLI changelog.](https://www.npmjs.com/package/mintlify?activeTab=versions)

View File

@@ -52,7 +52,7 @@ export const ALL = serve({
}
});
```
For more information about the connection object, refer to the [Setup](/setup/introduction) guide. In the
For more information about the connection object, refer to the [Database](/usage/database) guide. In the
special case of astro, you may also use your Astro DB credentials since it's also using LibSQL
under the hood. Refer to the [Astro DB documentation](https://docs.astro.build/en/guides/astro-db/) for more information.

View File

@@ -27,7 +27,7 @@ serve({
}
});
```
For more information about the connection object, refer to the [Setup](/setup/introduction) guide.
For more information about the connection object, refer to the [Database](/usage/database) guide.
Run the application using Bun by executing:
```bash

View File

@@ -31,7 +31,7 @@ export default serve({
})
});
```
For more information about the connection object, refer to the [Setup](/setup/introduction) guide.
For more information about the connection object, refer to the [Database](/usage/database) guide.
Now run the worker:
```bash

View File

@@ -0,0 +1,94 @@
---
title: 'Introduction'
description: 'Integrate bknd into your runtime/framework of choice'
---
import { cloudflare, nextjs, remix, astro, bun, node, docker, vite } from "/snippets/integration-icons.mdx"
## Start with a Framework
bknd seamlessly integrates with popular frameworks, allowing you to use what you're already familar with. The following guides will help you get started with your framework of choice.
<CardGroup cols={2}>
<Card
title="Next.js"
icon={<div className="text-primary-light">{nextjs}</div>}
href="/integration/nextjs"
/>
<Card
title="Remix"
icon={<div className="text-primary-light">{remix}</div>}
href="/integration/remix"
/>
<Card
title="Astro"
icon={<div className="text-primary-light">{astro}</div>}
href="/integration/astro"
/>
<Card
horizontal
title="Yours missing?"
href="https://github.com/bknd-io/bknd/issues/new"
>
Create a new issue to request a guide for your framework.
</Card>
</CardGroup>
## Start with a Runtime
If you prefer to use a runtime instead of a framework, you can choose from the following options. These runtimes allow you to serve the API and UI in the runtime's native server and serve the UI assets statically from `node_modules`.
<CardGroup cols={2}>
<Card
title="Node"
icon={<div className="text-primary-light">{node}</div>}
href="/integration/node"
/>
<Card
title="Bun"
icon={<div className="text-primary-light">{bun}</div>}
href="/integration/bun"
/>
<Card
title="Cloudflare"
icon={<div className="text-primary-light">{cloudflare}</div>}
href="/integration/cloudflare"
/>
<Card
title="Vite"
icon={<div className="text-primary-light">{vite}</div>}
href="/integration/vite"
/>
<Card
title="Docker"
icon={<div className="text-primary-light">{docker}</div>}
href="/integration/docker"
/>
<Card
horizontal
title="Yours missing?"
href="https://github.com/bknd-io/bknd/issues/new"
>
Create a new issue to request a guide for your runtime.
</Card>
</CardGroup>
## Overview
### Serving the backend (API)
Serve the backend as an API for any JS runtime or framework. The latter is especially handy, as it allows you to deploy your frontend and backend bundled together. Furthermore it allows adding additional logic in a way you're already familar with. Just add another route and you're good to go.
Here is an example of serving the API using node:
```js index.js
import { serve } from "bknd/adapter/node"
serve();
```
### Serving the Admin UI
The admin UI allows to manage your data including full configuration of your backend using a graphical user interface. Using `vite`, your admin route looks like this:
```tsx admin.tsx
import { Admin } from "bknd/ui"
import "bknd/dist/styles.css";
export default function AdminPage() {
return <Admin withProvider />
}
```

View File

@@ -28,7 +28,7 @@ export default serve({
}
});
```
For more information about the connection object, refer to the [Setup](/setup/introduction) guide.
For more information about the connection object, refer to the [Database](/usage/database) guide.
## Enabling the Admin UI
Create a file `[[...admin]].tsx` inside the `pages/admin` folder:

View File

@@ -29,7 +29,7 @@ const config = {
serve(config);
```
For more information about the connection object, refer to the [Setup](/setup/introduction) guide.
For more information about the connection object, refer to the [Database](/usage/database) guide.
Run the application using node by executing:
```bash

View File

@@ -26,7 +26,7 @@ const handler = serve({
export const loader = handler;
export const action = handler;
```
For more information about the connection object, refer to the [Setup](/setup/introduction) guide.
For more information about the connection object, refer to the [Database](/usage/database) guide.
Now make sure that you wrap your root layout with the `ClientProvider` so that all components
share the same context:

View File

@@ -33,7 +33,7 @@ export default serve({
}
})
```
For more information about the connection object, refer to the [Setup](/setup/introduction) guide.
For more information about the connection object, refer to the [Database](/usage/database) guide.
You can also run your vite server in `mode: "fresh"`, this will re-create the app on every fetch.
This is only useful for when working on the `bknd` repository directly.

View File

@@ -2,13 +2,26 @@
title: Introduction
---
import { cloudflare, nextjs, remix, astro, bun, node, docker, vite } 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).
<Note>
The documentation is currently a work in progress and not complete. Updates will be made soon.
The documentation is currently a work in progress and not complete. Updates will be made regularily.
</Note>
## Preview
**bknd** is so lightweight that it fully runs inside StackBlitz. Take a look at the preview below:
<Stackblitz {...examples.adminRich} />
<Accordion title="What's going on?" icon="lightbulb">
The example shown is starting a [node server](/integration/node) using an [in-memory database](/usage/database#sqlite-in-memory). To ensure there are a few entities defined, it is using an [initial structure](/usage/database#initial-structure) using the prototype methods. Furthermore it uses the [seed option](/usage/database#seeding-the-database) to seed some data in the structure created.
To ensure there are users defined on first boot, it hooks into the `App.Events.AppFirstBoot` event to create them (documentation pending).
</Accordion>
## Quickstart
Enter the following command to spin up an instance:
<CodeGroup>
@@ -20,7 +33,8 @@ Enter the following command to spin up an instance:
bunx bknd run
```
</CodeGroup>
To learn more about the CLI, check out the [Using the CLI](/cli) guide.
To learn more about the CLI, check out the [Using the CLI](/usage/cli) guide.
## Start with a Framework/Runtime
@@ -30,73 +44,42 @@ in the future, so stay tuned!
<CardGroup cols={2}>
<Card
title="Next.js"
icon={<div className="text-primary-light">
<svg xmlns="http://www.w3.org/2000/svg" width="28px" height="28px"
viewBox="0 0 24 24"><path fill="none" stroke="currentColor"
stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M9 15V9l7.745 10.65A9 9 0 1 1 19 17.657M15 12V9"/></svg></div>}
icon={<div className="text-primary-light">{nextjs}</div>}
href="/integration/nextjs"
/>
<Card
title="Remix"
icon={<div className="text-primary-light">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px"
viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M19.932
17.424c.18 2.31.18 3.394.18 4.576h-5.35c0-.258.004-.493.009-.732c.014-.743.03-1.517-.09-3.081c-.16-2.29-1.147-2.799-2.961-2.799H3.305v-4.166h8.67c2.291 0 3.437-.696 3.437-2.54c0-1.623-1.146-2.605-3.437-2.605h-8.67V2h9.624c5.189 0 7.767 2.449 7.767 6.36c0 2.926-1.814 4.834-4.265 5.152c2.069.413 3.278 1.59 3.501 3.912" clip-rule="evenodd"/><path fill="currentColor" d="M3.305 22v-3.106h5.657c.945 0 1.15.7 1.15 1.118V22z"/></svg>
</div>}
icon={<div className="text-primary-light">{remix}</div>}
href="/integration/remix"
/>
<Card
title="Astro"
icon={<div className="text-primary-light">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<rect width="24" height="24" fill="none"/><path fill="currentColor" d="M9.24 19.035c-.901-.826-1.164-2.561-.789-3.819c.65.793 1.552 1.044 2.486 1.186c1.44.218 2.856.137 4.195-.524c.153-.076.295-.177.462-.278c.126.365.159.734.115 1.11c-.107.915-.56 1.622-1.283 2.158c-.289.215-.594.406-.892.608c-.916.622-1.164 1.35-.82 2.41l.034.114a2.4 2.4 0 0 1-1.07-.918a2.6 2.6 0 0 1-.412-1.401c-.003-.248-.003-.497-.036-.74c-.081-.595-.36-.86-.883-.876a1.034 1.034 0 0 0-1.075.843q-.013.058-.033.126M4.1 15.007s2.666-1.303 5.34-1.303l2.016-6.26c.075-.304.296-.51.544-.51c.25 0 .47.206.545.51l2.016 6.26c3.167 0 5.34 1.303 5.34 1.303L15.363 2.602c-.13-.366-.35-.602-.645-.602H9.283c-.296 0-.506.236-.645.602c-.01.024-4.538 12.405-4.538 12.405"/>
</svg>
</div>}
icon={<div className="text-primary-light">{astro}</div>}
href="/integration/astro"
/>
<Card
title="Node"
icon={<div className="text-primary-light">{node}</div>}
href="/integration/node"
/>
<Card
title="Cloudflare"
icon={<div className="text-primary-light">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 128 128">
<path fill="currentColor" d="M30.743 116.257a15.5 15.5 0 0 1-3.42-4.107L4.068 71.752a15.56 15.56 0 0 1 0-15.503L27.323 15.85a15.5 15.5 0 0 1 3.417-4.084l15.601 28.166l-11.266 20.282a7.75 7.75 0 0 0 0 7.543l11.277 20.349zM100.665 15.85l23.255 40.398a15.49 15.49 0 0 1 0 15.503l-23.255 40.398a15.5 15.5 0 0 1-13.416 7.752H68.994l28.92-52.145a7.75 7.75 0 0 0 0-7.513L68.994 8.099h18.255a15.5 15.5 0 0 1 13.416 7.751M36.119 9.139a15.5 15.5 0 0 1 5.562-1.041h21.255l28.92 52.145a7.75 7.75 0 0 1 0 7.513l-28.92 52.145H41.682c-2.062 0-4.124-.423-5.993-1.193L63.406 67.29c.894-1.61 1.002-4.738.107-6.348z"/>
</svg>
</div>}
icon={<div className="text-primary-light">{cloudflare}</div>}
href="/integration/cloudflare"
/>
<Card
title="Bun"
icon={<div className="text-primary-light">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24">
<path fill="currentColor" d="M12 22.596c6.628 0 12-4.338 12-9.688c0-3.318-2.057-6.248-5.219-7.986c-1.286-.715-2.297-1.357-3.139-1.89C14.058 2.025 13.08 1.404 12 1.404c-1.097 0-2.334.785-3.966 1.821a50 50 0 0 1-2.816 1.697C2.057 6.66 0 9.59 0 12.908c0 5.35 5.372 9.687 12 9.687zM10.599 4.715c.334-.759.503-1.58.498-2.409c0-.145.202-.187.23-.029c.658 2.783-.902 4.162-2.057 4.624c-.124.048-.199-.121-.103-.209a5.8 5.8 0 0 0 1.432-1.977m2.058-.102a5.8 5.8 0 0 0-.782-2.306v-.016c-.069-.123.086-.263.185-.172c1.962 2.111 1.307 4.067.556 5.051c-.082.103-.23-.003-.189-.126a5.85 5.85 0 0 0 .23-2.431m1.776-.561a5.7 5.7 0 0 0-1.612-1.806v-.014c-.112-.085-.024-.274.114-.218c2.595 1.087 2.774 3.18 2.459 4.407a.12.12 0 0 1-.049.071a.11.11 0 0 1-.153-.026a.12.12 0 0 1-.022-.083a5.9 5.9 0 0 0-.737-2.331m-5.087.561c-.617.546-1.282.76-2.063 1c-.117 0-.195-.078-.156-.181c1.752-.909 2.376-1.649 2.999-2.778c0 0 .155-.118.188.085c0 .304-.349 1.329-.968 1.874m4.945 11.237a2.96 2.96 0 0 1-.937 1.553c-.346.346-.8.565-1.286.62a2.18 2.18 0 0 1-1.327-.62a2.96 2.96 0 0 1-.925-1.553a.24.24 0 0 1 .064-.198a.23.23 0 0 1 .193-.069h3.965a.23.23 0 0 1 .19.07c.05.053.073.125.063.197m-5.458-2.176a1.86 1.86 0 0 1-2.384-.245a1.98 1.98 0 0 1-.233-2.447c.207-.319.503-.566.848-.713a1.84 1.84 0 0 1 1.092-.11c.366.075.703.261.967.531a1.98 1.98 0 0 1 .408 2.114a1.93 1.93 0 0 1-.698.869zm8.495.005a1.86 1.86 0 0 1-2.381-.253a1.96 1.96 0 0 1-.547-1.366c0-.384.11-.76.32-1.079c.207-.319.503-.567.849-.713a1.84 1.84 0 0 1 1.093-.108c.367.076.704.262.968.534a1.98 1.98 0 0 1 .4 2.117a1.93 1.93 0 0 1-.702.868"/>
</svg>
</div>}
icon={<div className="text-primary-light">{bun}</div>}
href="/integration/bun"
/>
<Card
title="Node"
icon={<div className="text-primary-light">
<svg xmlns="http://www.w3.org/2000/svg" width={24} height={24} viewBox="0 0 24 24">
<path fill="currentColor" d="M12 23.956c-.342 0-.66-.089-.957-.243l-3.029-1.738c-.455-.242-.227-.33-.09-.374c.614-.198.728-.242 1.366-.595c.068-.044.16-.022.228.022l2.323 1.343c.09.044.205.044.273 0l9.087-5.084c.09-.044.136-.132.136-.242V6.899c0-.11-.045-.198-.136-.242l-9.087-5.061c-.091-.044-.205-.044-.273 0L2.754 6.657c-.091.044-.137.154-.137.242v10.146c0 .088.046.198.137.242l2.482 1.387c1.344.66 2.186-.11 2.186-.88V7.78c0-.132.114-.264.274-.264h1.161c.137 0 .273.11.273.264v10.013c0 1.739-.979 2.751-2.687 2.751c-.524 0-.934 0-2.095-.55l-2.391-1.32A1.85 1.85 0 0 1 1 17.067V6.921c0-.66.364-1.276.957-1.606L11.044.23a2.1 2.1 0 0 1 1.912 0l9.088 5.084c.592.33.956.946.956 1.606v10.146c0 .66-.364 1.276-.956 1.607l-9.087 5.083a2.4 2.4 0 0 1-.957.198m2.801-6.977c-3.985 0-4.805-1.76-4.805-3.257c0-.132.114-.264.273-.264h1.184c.137 0 .25.088.25.22c.183 1.166.707 1.738 3.121 1.738c1.913 0 2.733-.418 2.733-1.408c0-.572-.228-.99-3.211-1.276c-2.483-.243-4.031-.77-4.031-2.685c0-1.783 1.548-2.84 4.145-2.84c2.915 0 4.35.969 4.532 3.082a.35.35 0 0 1-.069.198c-.045.044-.113.088-.182.088h-1.184a.265.265 0 0 1-.25-.198c-.274-1.21-.98-1.607-2.847-1.607c-2.096 0-2.346.704-2.346 1.233c0 .638.296.836 3.12 1.188c2.801.352 4.122.858 4.122 2.75c-.023 1.938-1.662 3.038-4.555 3.038"></path>
</svg>
</div>}
href="/integration/node"
/>
<Card
title="Vite"
icon={<div className="text-primary-light">
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24">
<rect width="24" height="24" fill="none"/><path fill="currentColor" d="m8.525 4.63l-5.132-.915a1.17 1.17 0 0 0-1.164.468a1.16 1.16 0 0 0-.07 1.28l8.901 15.58a1.182 1.182 0 0 0 2.057-.008l8.729-15.578c.49-.875-.262-1.917-1.242-1.739l-4.574.813l-.206.754l4.906-.871a.474.474 0 0 1 .498.697L12.5 20.689a.47.47 0 0 1-.5.234a.47.47 0 0 1-.326-.231L2.772 5.112a.474.474 0 0 1 .496-.7l5.133.916l.074.013z"/><path fill="currentColor" d="m15.097 5.26l.162-.593l-.6.107zm-5.88-.506l.513.09l-.542.427z"/><path fill="currentColor" d="m15.549 2.367l-6.1 1.26a.22.22 0 0 0-.126.077a.25.25 0 0 0-.055.142l-.375 6.685a.24.24 0 0 0 .079.194a.21.21 0 0 0 .195.05l1.698-.414c.16-.038.302.11.27.278l-.505 2.606c-.034.176.122.326.285.274l1.049-.336c.162-.052.319.098.284.274l-.801 4.093c-.05.257.272.396.407.177l.09-.147l4.97-10.464c.084-.175-.06-.375-.242-.338l-1.748.356c-.165.034-.304-.128-.258-.297l1.14-4.173c.047-.17-.093-.331-.257-.297"/>
</svg>
</div>}
icon={<div className="text-primary-light">{vite}</div>}
href="/integration/vite"
/>
<Card
title="Docker"
icon={<div className="text-primary-light">
<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>
</div>}
icon={<div className="text-primary-light">{docker}</div>}
href="/integration/docker"
/>
</CardGroup>

View File

@@ -61,11 +61,43 @@
"navigation": [
{
"group": "Getting Started",
"pages": ["introduction", "sdk", "react", "cli"]
"pages": ["introduction", "motivation"]
},
{
"group": "Setup",
"pages": ["setup/introduction", "setup/database"]
"group": "Usage",
"pages": [
"usage/introduction",
"usage/database",
"usage/cli",
"usage/sdk",
"usage/react",
"usage/elements"
]
},
{
"group": "Integration",
"pages": [
"integration/introduction",
{
"group": "Frameworks",
"pages": [
"integration/nextjs",
"integration/remix",
"integration/astro",
"integration/vite"
]
},
{
"group": "Runtimes",
"pages": [
"integration/node",
"integration/bun",
"integration/cloudflare",
"integration/deno",
"integration/docker"
]
}
]
},
{
"group": "Modules",
@@ -78,21 +110,6 @@
"modules/flows"
]
},
{
"group": "Integration",
"pages": [
"integration/extending",
"integration/nextjs",
"integration/remix",
"integration/cloudflare",
"integration/bun",
"integration/astro",
"integration/node",
"integration/deno",
"integration/vite",
"integration/docker"
]
},
{
"group": "Deployment",
"pages": [

71
docs/motivation.mdx Normal file
View File

@@ -0,0 +1,71 @@
---
title: "Motivation"
description: "Why another backend system?"
---
Creating digital products always requires developing both the backend (the logic) and the frontend (the appearance). Building a backend from scratch demands deep knowledge in areas such as authentication and database management. Using a backend framework can speed up initial development, but it still requires ongoing effort to work within its constraints (e.g., *"how to do X with Y?"*), which can quickly slow you down. Choosing a backend system is a tough decision, as you might not be aware of its limitations until you encounter them.
<Check>
**The solution:** A backend system that only assumes and implements primitive details, integrates into multiple environments, and adheres to industry standards.
</Check>
For the sake of brevity, let's assume you are looking for a "backend system" rather than dealing with custom implementations yourself. Let's identify the most common challenges:
1. Database lock-in
2. Environment and framework lock-in
3. Deviation from standards (such as `X-Auth` headers for authentication)
4. *Wrong-for-your-use-case* implementations
5. Complex self-hosting
## Database lock-in
As the developer of a backend system, you must make tough decisions, one of which is choosing which database(s) to support. To simplify development, many systems lock you into a single database, leveraging its advanced features.
But isn't the database known to be the hardest part to scale? Isn't more logic moving to the application layer? Haven't NoSQL databases proven this? If you're like me, you may have dipped your toes into the NoSQL world only to quickly return to SQL. SQL is known, predictable, and safe. But what if we could have both? NoSQL offers flexibility and scalability, yet querying it is tedious due to vendor-specific implementations.
To get the best of both worlds, bknd focuses on the weakest SQL database (SQLite), treating it as a data store and query interface. Schema details and enforcement are moved to the application layer, making it easy to adjust a default value or property length. The added benefit is that any SQL database could theoretically work the same way, and since it's all TypeScript, the same validation logic can be used on both the client and server sidesyou can validate your data before it even reaches your server. It even works without database-enforced referential integrity, as the integrity checks occur on the application layer. This opens the door to NewSQL systems like PlanetScale.
## Environment and framework lock-in
There are backend systems that embed themselves into a specific React framework. This works well until you realize it doesn't support your preferred framework or the new hyped one you're considering switching to. Just like database choices, decisions must be made. The easiest path is to select a single option and let people live with it.
Alternatively, you could develop for the weakest environment (workerd) by strictly using Web APIs, avoiding shortcuts, and implementing certain logic manually because the go-to package is using Node APIs. This isn't always fun, but it's essential. The benefit? It works anywhere JavaScript does.
bknd is the only backend system that not only works with any JavaScript framework but also integrates directly into it. It runs within the framework, enabling a single deployment for your entire app.
*"But isn't it ironic that it forces a JavaScript environment?"* you might ask. And you're right, but it also allows running standalone via CLI or Docker.
## Deviation from standards
One of the biggest frustrations I've encountered is when software vendors choose custom headers for authentication or implement query parameters in a format they find more suitable—such as unencoded JSON for simplicity. When you are in full control, it's tempting to use a more suitable format, or just use an auth-ish name for the header propertyafter all, it's just a header, right?
The issue is that users may rely on HTTP clients that offer built-in authentication methods, which won't include your custom solution. Custom `SearchParams` implementations might be convenient, but translating them across different environments and languages can be challenging without trial and error.
bknd strives to adhere to web standards as much as possible while offering handy alternatives. Here's an example of the `select` search parameter for retrieving a list of entities:
```bash
/api/data/todos?select=id&select=name # web standard
/api/data/todos?select=id,name # handy alternative
```
If you ever find an instance where bknd isn't adhering to standards or could be improved, please feel free to [file an issue](https://github.com/bknd-io/bknd/issues/new). Your feedback is greatly appreciated!
## Wrong-for-your-use-case implementations
If you've ever developed a social chat application, you likely discovered the extensive feature depth required—features we often take for granted. Things like socket connections for single and group chats, partial loading, asset attachments, and emoji reactions. Even more frustrating, these features make the app being considered incomplete until delivered.
The same applies to backend systems. Features such as email sending, password resets, and image transformations are expected. Worse still, you'll receive feedback requesting different email verification methods—PIN codes instead of links, 4-digit codes versus 6-digit ones, or UUIDs like Axiom uses.
Since it's impossible to satisfy all requirements, why implement them at all? *"Because people expect it."* That's fair. But technically, email verification is not a core backend feature—it's business logic. Setting it up involves:
1. Adding a `code` and `verified` field to the users' entity and generating a random code on creation.
2. Creating an endpoint to accept the code, retrieve the authenticated user, check the code, clear it, and mark the user as verified.
Additional security measures, such as short-lived tokens, can be added, but the concept remains simple.
Instead of hardcoding such features, bknd offers a powerful event system that supports asynchronous (like webhooks) and synchronous execution, blocking further actions if needed. With integrated workflows (UI coming soon), you can listen to and react to system events, and even map them to endpoints. Since workflows, like everything else in bknd, are JSON-serializable, they're easy to export and import.
## Complex self-hosting
Finally, hosting. It's a business advantage if your system is highly sought after but difficult to self-host, forcing users to opt for your cloud service. The truth is, if it's hard for users, it's also hard for the vendor, which drives up costs.
If you know how to deploy your Next.js, Remix, or Astro application, you can deploy bknd. It's straightforward to deploy using Cloudflare Workers/Pages or with just 28 lines of a Dockerfile. No PhD required.

View File

@@ -0,0 +1,31 @@
export const nextjs = <svg xmlns="http://www.w3.org/2000/svg" width="28px" height="28px"
viewBox="0 0 24 24"><path fill="none" stroke="currentColor"
stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M9 15V9l7.745 10.65A9 9 0 1 1 19 17.657M15 12V9"/></svg>
export const remix = <svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px"
viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M19.932
17.424c.18 2.31.18 3.394.18 4.576h-5.35c0-.258.004-.493.009-.732c.014-.743.03-1.517-.09-3.081c-.16-2.29-1.147-2.799-2.961-2.799H3.305v-4.166h8.67c2.291 0 3.437-.696 3.437-2.54c0-1.623-1.146-2.605-3.437-2.605h-8.67V2h9.624c5.189 0 7.767 2.449 7.767 6.36c0 2.926-1.814 4.834-4.265 5.152c2.069.413 3.278 1.59 3.501 3.912" clip-rule="evenodd"/><path fill="currentColor" d="M3.305 22v-3.106h5.657c.945 0 1.15.7 1.15 1.118V22z"/></svg>;
export const astro = <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<rect width="24" height="24" fill="none"/><path fill="currentColor" d="M9.24 19.035c-.901-.826-1.164-2.561-.789-3.819c.65.793 1.552 1.044 2.486 1.186c1.44.218 2.856.137 4.195-.524c.153-.076.295-.177.462-.278c.126.365.159.734.115 1.11c-.107.915-.56 1.622-1.283 2.158c-.289.215-.594.406-.892.608c-.916.622-1.164 1.35-.82 2.41l.034.114a2.4 2.4 0 0 1-1.07-.918a2.6 2.6 0 0 1-.412-1.401c-.003-.248-.003-.497-.036-.74c-.081-.595-.36-.86-.883-.876a1.034 1.034 0 0 0-1.075.843q-.013.058-.033.126M4.1 15.007s2.666-1.303 5.34-1.303l2.016-6.26c.075-.304.296-.51.544-.51c.25 0 .47.206.545.51l2.016 6.26c3.167 0 5.34 1.303 5.34 1.303L15.363 2.602c-.13-.366-.35-.602-.645-.602H9.283c-.296 0-.506.236-.645.602c-.01.024-4.538 12.405-4.538 12.405"/>
</svg>
export const cloudflare = <svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 128 128">
<path fill="currentColor" d="M30.743 116.257a15.5 15.5 0 0 1-3.42-4.107L4.068 71.752a15.56 15.56 0 0 1 0-15.503L27.323 15.85a15.5 15.5 0 0 1 3.417-4.084l15.601 28.166l-11.266 20.282a7.75 7.75 0 0 0 0 7.543l11.277 20.349zM100.665 15.85l23.255 40.398a15.49 15.49 0 0 1 0 15.503l-23.255 40.398a15.5 15.5 0 0 1-13.416 7.752H68.994l28.92-52.145a7.75 7.75 0 0 0 0-7.513L68.994 8.099h18.255a15.5 15.5 0 0 1 13.416 7.751M36.119 9.139a15.5 15.5 0 0 1 5.562-1.041h21.255l28.92 52.145a7.75 7.75 0 0 1 0 7.513l-28.92 52.145H41.682c-2.062 0-4.124-.423-5.993-1.193L63.406 67.29c.894-1.61 1.002-4.738.107-6.348z"/>
</svg>
export const bun = <svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24">
<path fill="currentColor" d="M12 22.596c6.628 0 12-4.338 12-9.688c0-3.318-2.057-6.248-5.219-7.986c-1.286-.715-2.297-1.357-3.139-1.89C14.058 2.025 13.08 1.404 12 1.404c-1.097 0-2.334.785-3.966 1.821a50 50 0 0 1-2.816 1.697C2.057 6.66 0 9.59 0 12.908c0 5.35 5.372 9.687 12 9.687zM10.599 4.715c.334-.759.503-1.58.498-2.409c0-.145.202-.187.23-.029c.658 2.783-.902 4.162-2.057 4.624c-.124.048-.199-.121-.103-.209a5.8 5.8 0 0 0 1.432-1.977m2.058-.102a5.8 5.8 0 0 0-.782-2.306v-.016c-.069-.123.086-.263.185-.172c1.962 2.111 1.307 4.067.556 5.051c-.082.103-.23-.003-.189-.126a5.85 5.85 0 0 0 .23-2.431m1.776-.561a5.7 5.7 0 0 0-1.612-1.806v-.014c-.112-.085-.024-.274.114-.218c2.595 1.087 2.774 3.18 2.459 4.407a.12.12 0 0 1-.049.071a.11.11 0 0 1-.153-.026a.12.12 0 0 1-.022-.083a5.9 5.9 0 0 0-.737-2.331m-5.087.561c-.617.546-1.282.76-2.063 1c-.117 0-.195-.078-.156-.181c1.752-.909 2.376-1.649 2.999-2.778c0 0 .155-.118.188.085c0 .304-.349 1.329-.968 1.874m4.945 11.237a2.96 2.96 0 0 1-.937 1.553c-.346.346-.8.565-1.286.62a2.18 2.18 0 0 1-1.327-.62a2.96 2.96 0 0 1-.925-1.553a.24.24 0 0 1 .064-.198a.23.23 0 0 1 .193-.069h3.965a.23.23 0 0 1 .19.07c.05.053.073.125.063.197m-5.458-2.176a1.86 1.86 0 0 1-2.384-.245a1.98 1.98 0 0 1-.233-2.447c.207-.319.503-.566.848-.713a1.84 1.84 0 0 1 1.092-.11c.366.075.703.261.967.531a1.98 1.98 0 0 1 .408 2.114a1.93 1.93 0 0 1-.698.869zm8.495.005a1.86 1.86 0 0 1-2.381-.253a1.96 1.96 0 0 1-.547-1.366c0-.384.11-.76.32-1.079c.207-.319.503-.567.849-.713a1.84 1.84 0 0 1 1.093-.108c.367.076.704.262.968.534a1.98 1.98 0 0 1 .4 2.117a1.93 1.93 0 0 1-.702.868"/>
</svg>
export const node = <svg xmlns="http://www.w3.org/2000/svg" width={24} height={24} viewBox="0 0 24 24">
<path fill="currentColor" d="M12 23.956c-.342 0-.66-.089-.957-.243l-3.029-1.738c-.455-.242-.227-.33-.09-.374c.614-.198.728-.242 1.366-.595c.068-.044.16-.022.228.022l2.323 1.343c.09.044.205.044.273 0l9.087-5.084c.09-.044.136-.132.136-.242V6.899c0-.11-.045-.198-.136-.242l-9.087-5.061c-.091-.044-.205-.044-.273 0L2.754 6.657c-.091.044-.137.154-.137.242v10.146c0 .088.046.198.137.242l2.482 1.387c1.344.66 2.186-.11 2.186-.88V7.78c0-.132.114-.264.274-.264h1.161c.137 0 .273.11.273.264v10.013c0 1.739-.979 2.751-2.687 2.751c-.524 0-.934 0-2.095-.55l-2.391-1.32A1.85 1.85 0 0 1 1 17.067V6.921c0-.66.364-1.276.957-1.606L11.044.23a2.1 2.1 0 0 1 1.912 0l9.088 5.084c.592.33.956.946.956 1.606v10.146c0 .66-.364 1.276-.956 1.607l-9.087 5.083a2.4 2.4 0 0 1-.957.198m2.801-6.977c-3.985 0-4.805-1.76-4.805-3.257c0-.132.114-.264.273-.264h1.184c.137 0 .25.088.25.22c.183 1.166.707 1.738 3.121 1.738c1.913 0 2.733-.418 2.733-1.408c0-.572-.228-.99-3.211-1.276c-2.483-.243-4.031-.77-4.031-2.685c0-1.783 1.548-2.84 4.145-2.84c2.915 0 4.35.969 4.532 3.082a.35.35 0 0 1-.069.198c-.045.044-.113.088-.182.088h-1.184a.265.265 0 0 1-.25-.198c-.274-1.21-.98-1.607-2.847-1.607c-2.096 0-2.346.704-2.346 1.233c0 .638.296.836 3.12 1.188c2.801.352 4.122.858 4.122 2.75c-.023 1.938-1.662 3.038-4.555 3.038"></path>
</svg>
export const vite = <svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24">
<rect width="24" height="24" fill="none"/><path fill="currentColor" d="m8.525 4.63l-5.132-.915a1.17 1.17 0 0 0-1.164.468a1.16 1.16 0 0 0-.07 1.28l8.901 15.58a1.182 1.182 0 0 0 2.057-.008l8.729-15.578c.49-.875-.262-1.917-1.242-1.739l-4.574.813l-.206.754l4.906-.871a.474.474 0 0 1 .498.697L12.5 20.689a.47.47 0 0 1-.5.234a.47.47 0 0 1-.326-.231L2.772 5.112a.474.474 0 0 1 .496-.7l5.133.916l.074.013z"/><path fill="currentColor" d="m15.097 5.26l.162-.593l-.6.107zm-5.88-.506l.513.09l-.542.427z"/><path fill="currentColor" d="m15.549 2.367l-6.1 1.26a.22.22 0 0 0-.126.077a.25.25 0 0 0-.055.142l-.375 6.685a.24.24 0 0 0 .079.194a.21.21 0 0 0 .195.05l1.698-.414c.16-.038.302.11.27.278l-.505 2.606c-.034.176.122.326.285.274l1.049-.336c.162-.052.319.098.284.274l-.801 4.093c-.05.257.272.396.407.177l.09-.147l4.97-10.464c.084-.175-.06-.375-.242-.338l-1.748.356c-.165.034-.304-.128-.258-.297l1.14-4.173c.047-.17-.093-.331-.257-.297"/>
</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>

View File

@@ -0,0 +1,45 @@
export const examples = {
adminRich: {
path: "github/bknd-io/bknd-examples",
startScript: "example-admin-rich",
initialPath: "/data/schema"
}
}
export const Stackblitz = ({
path = "github/bknd-io/bknd-examples",
ratio = 9/16,
...props
}) => {
const params = new URLSearchParams({
ctl: 1,
hideExplorer: 1,
embed: 1,
view: "preview",
...props
})
const url = new URL(`https://stackblitz.com/${path}?${params.toString()}`)
return (
<>
<div style={{
position: "relative",
overflow: "hidden",
width: '100%',
paddingTop: `${ratio*100}%`
}}>
<iframe width="100%" height="100%" src={url.toString()}
style={{
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
border: "none"
}}
></iframe>
</div>
<div style={{ fontSize: "80%", opacity: 0.7, marginTop: "0.2rem", marginBottom: "1rem", textAlign: "center" }}>If you're having issues viewing it inline, <a href={url.toString()} target="_blank">try in a new tab</a>.</div>
</>
)
}

View File

@@ -12,9 +12,21 @@ the dependencies.
</Note>
## Database
### SQLite in-memory
The easiest to get started is using SQLite in-memory. When serving the API in the "Integrations",
the function accepts an object with connection details. To use an in-memory database, you can either omit the object completely or explicitly use it as follows:
```json
{
"type": "libsql",
"config": {
"url": ":memory:"
}
}
```
### SQLite as file
The easiest to get started is using SQLite as a file. When serving the API in the "Integrations",
the function accepts an object with connection details. To use a file, use the following:
Just like the in-memory option, using a file is just as easy:
```json
{
"type": "libsql",
@@ -156,11 +168,11 @@ const { data: posts } = await api.data.readMany("posts", {})
// `posts` is now typed as Database["posts"]
```
The type completion is available for the API as well as all provided [React hooks](/react).
The type completion is available for the API as well as all provided [React hooks](/usage/react).
### Seeding the database
To seed your database with initial data, you can pass a `seed` function to the configuration. It
provides the `ModuleBuildContext` ([reference](/setup/introduction#modulebuildcontext)) as the first argument.
provides the `ModuleBuildContext` ([reference](/usage/introduction#modulebuildcontext)) as the first argument.
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.

25
docs/usage/elements.mdx Normal file
View File

@@ -0,0 +1,25 @@
---
title: "React Elements"
description: "Speed up your frontend development"
---
<Note>
The documentation is currently a work in progress and not complete.
</Note>
Not only creating and maintaing a backend is time-consuming, but also integrating it into your frontend can be a hassle. With `bknd/elements`, you can easily add media uploads and authentication forms to your app without having to figure out API details.
## Media uploads
```tsx
import { Media } from "bknd/elements"
export function UserAvatar() {
return <Media.Dropzone
entity={{ name: "users", id: 1, field: "avatar" }}
maxItems={1}
overwrite
/>
}
```

View File

@@ -4,7 +4,7 @@ description: 'Setting up bknd'
---
There are several methods to get **bknd** up and running. You can choose between these options:
1. [Run it using the CLI](/cli): That's the easiest and fastest way to get started.
1. [Run it using the CLI](/usage/cli): That's the easiest and fastest way to get started.
2. Use a runtime like [Node](/integration/node), [Bun](/integration/bun) or
[Cloudflare](/integration/cloudflare) (workerd). This will run the API and UI in the runtime's
native server and serves the UI assets statically from `node_modules`.
@@ -78,7 +78,7 @@ const connection = {
```
Alternatively, you can pass an instance of a `Connection` class directly,
see [Custom Connection](/setup/database#custom-connection) as a reference.
see [Custom Connection](/usage/database#custom-connection) as a reference.
If the connection object is omitted, the app will try to use an in-memory database.
@@ -160,7 +160,7 @@ To validate your configuration against a JSON schema, you can also dump the sche
npx bknd schema
```
To create an initial data structure, you can use helpers [described here](/setup/database#initial-structure).
To create an initial data structure, you can use helpers [described here](/usage/database#initial-structure).
### `plugins`
The `plugins` property is an array of functions that are called after the app has been built,

View File

@@ -3,10 +3,15 @@ title: 'SDK (React)'
description: 'Use the bknd SDK for React'
---
For all SDK options targeting React, you always have 2 options:
1. use simple hooks which are solely based on the [API](/sdk)
2. use the query hook that makes wraps the API in [SWR](https://swr.vercel.app/)
bknd exports 4 useful hooks to work with your backend:
1. simple hooks which are solely based on the [API](/usage/sdk):
- [`useApi`](#useapi)
- [`useEntity`](#useentity)
2. query hooks that wraps the API in [SWR](https://swr.vercel.app/):
- [`useApiQuery`](#useapiquery)
- [`useEntityQuery`](#useentityquery)
## `useApi()`
To use the simple hook that returns the Api, you can use:
```tsx
import { useApi } from "bknd/client";
@@ -17,7 +22,7 @@ export default function App() {
}
```
## `useApiQuery([selector], [options])`
## `useApiQuery()`
This hook wraps the API class in an SWR hook for convenience. You can use any API endpoint
supported, like so:
```tsx
@@ -41,12 +46,12 @@ export default function App() {
* `options`: optional object that inherits from `SWRConfiguration`
```ts
type Options<Data> = SWRConfiguration & {
enabled?: boolean;
refine?: (data: Data) => Data | any;
}
```
```ts
type Options <Data> = import("swr").SWRConfiguration & {
enabled? : boolean;
refine? : (data: Data) => Data | any;
}
```
* `enabled`: Determines whether this hook should trigger a fetch of the data or not.
* `refine`: Optional refinement that is called after a response from the API has been
@@ -58,7 +63,7 @@ following example we'll also use a `refine` function as well as `revalidateOnFoc
`SWRConfiguration`) so that our data keeps updating on window focus change.
```tsx
import { useState } from "react";
import { useEffect, useState } from "react";
import { useApiQuery } from "bknd/client";
export default function App() {
@@ -108,7 +113,7 @@ export default function App() {
## `useEntity()`
This hook wraps the endpoints of `DataApi` and returns CRUD options as parameters:
```tsx
import { useState } from "react",
import { useState, useEffect } from "react";
import { useEntity } from "bknd/client";
export default function App() {
@@ -144,7 +149,6 @@ optional. Deletes the given entry.
This hook wraps the actions from `useEntity` around `SWR`. The previous example would look like
this:
```tsx
import { useState } from "react",
import { useEntityQuery } from "bknd/client";
export default function App() {
@@ -158,7 +162,7 @@ export default function App() {
All actions returned from `useEntityQuery` are conveniently wrapped around the `mutate` function,
so you don't have think about this:
```tsx
import { useState } from "react";
import { useState, useEffect } from "react";
import { useEntityQuery } from "bknd/client";
export default function App() {

View File

@@ -10,6 +10,9 @@ import { Api } from "bknd";
const api = new Api({
host: "..." // point to your bknd instance
});
// make sure to verify auth
await api.verifyAuth();
```
The `Api` class is the main entry point for interacting with the bknd API. It provides methods