mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-15 20:17:22 +00:00
docs: added docs about how to use bknd.config.ts
This commit is contained in:
@@ -3,7 +3,6 @@ import { isNode } from "bknd/utils";
|
|||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
|
|
||||||
type NextjsEnv = NextApiRequest["env"];
|
type NextjsEnv = NextApiRequest["env"];
|
||||||
|
|
||||||
export type NextjsBkndConfig<Env = NextjsEnv> = FrameworkBkndConfig<Env> & {
|
export type NextjsBkndConfig<Env = NextjsEnv> = FrameworkBkndConfig<Env> & {
|
||||||
cleanRequest?: { searchParams?: string[] };
|
cleanRequest?: { searchParams?: string[] };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,47 +1,46 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"types": ["bun-types", "@cloudflare/workers-types"],
|
"types": ["bun-types", "@cloudflare/workers-types"],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"allowImportingTsExtensions": false,
|
"allowImportingTsExtensions": false,
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"allowUnusedLabels": false,
|
"allowUnusedLabels": false,
|
||||||
"allowUnreachableCode": false,
|
"allowUnreachableCode": false,
|
||||||
"exactOptionalPropertyTypes": false,
|
"exactOptionalPropertyTypes": false,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noImplicitOverride": true,
|
"noImplicitOverride": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noPropertyAccessFromIndexSignature": false,
|
"noPropertyAccessFromIndexSignature": false,
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
"noUnusedLocals": false,
|
"noUnusedLocals": false,
|
||||||
"noUnusedParameters": false,
|
"noUnusedParameters": false,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"outDir": "./dist/types",
|
"outDir": "./dist/types",
|
||||||
"paths": {
|
"paths": {
|
||||||
"*": ["./src/*"],
|
"*": ["./src/*"]
|
||||||
"bknd": ["./src/*"]
|
}
|
||||||
}
|
},
|
||||||
},
|
"include": [
|
||||||
"include": [
|
"./src/**/*.ts",
|
||||||
"./src/**/*.ts",
|
"./src/**/*.tsx",
|
||||||
"./src/**/*.tsx",
|
"vite.dev.ts",
|
||||||
"vite.dev.ts",
|
"build.ts",
|
||||||
"build.ts",
|
"build.cli.ts",
|
||||||
"build.cli.ts",
|
"__test__",
|
||||||
"__test__",
|
"e2e/**/*.ts"
|
||||||
"e2e/**/*.ts"
|
],
|
||||||
],
|
"exclude": ["node_modules", "dist", "dist/types", "**/*.d.ts"]
|
||||||
"exclude": ["node_modules", "dist", "dist/types", "**/*.d.ts"]
|
|
||||||
}
|
}
|
||||||
|
|||||||
153
docs/extending/config.mdx
Normal file
153
docs/extending/config.mdx
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
---
|
||||||
|
title: bknd.config.ts
|
||||||
|
---
|
||||||
|
|
||||||
|
The central configuration file to extend bknd should be placed in the root of your project, so that the [CLI](/usage/cli#using-configuration-file-bknd-config) can automatically pick it up. It allows to:
|
||||||
|
* define your database connection centrally
|
||||||
|
* pass in initial configuration or data seeds when booting the first time
|
||||||
|
* add plugins to the app
|
||||||
|
* hook into system events
|
||||||
|
* define custom routes and endpoints
|
||||||
|
|
||||||
|
|
||||||
|
A simple example of a `bknd.config.ts` file:
|
||||||
|
|
||||||
|
```typescript bknd.config.ts
|
||||||
|
import type { BkndConfig } from "bknd/adapter";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
connection: {
|
||||||
|
url: "file:data.db",
|
||||||
|
}
|
||||||
|
} satisfies BkndConfig;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `BkndConfig` extends the [`CreateAppConfig`](/usage/introduction#configuration-createappconfig) type with the following properties:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export type BkndConfig = CreateAppConfig & {
|
||||||
|
// return the app configuration as object or from a function
|
||||||
|
app?: CreateAppConfig | ((args: Args) => CreateAppConfig);
|
||||||
|
// called before the app is built
|
||||||
|
beforeBuild?: (app: App) => Promise<void>;
|
||||||
|
// called after the app has been built
|
||||||
|
onBuilt?: (app: App) => Promise<void>;
|
||||||
|
// passed as the first argument to the `App.build` method
|
||||||
|
buildConfig?: Parameters<App["build"]>[0];
|
||||||
|
// force the app to be recreated
|
||||||
|
force?: boolean;
|
||||||
|
// the id of the app, defaults to `app`
|
||||||
|
id?: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
The supported configuration file extensions are `js`, `ts`, `mjs`, `cjs` and `json`. Throughout the documentation, we'll use `ts` for the file extension.
|
||||||
|
|
||||||
|
### `app` (CreateAppConfig)
|
||||||
|
The `app` property is a function that returns a `CreateAppConfig` object. It allows to pass in the environment variables to the configuration object.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { BkndConfig } from "bknd/adapter";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
app: ({ env }) => ({
|
||||||
|
connection: {
|
||||||
|
url: env.DB_URL,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} satisfies BkndConfig;
|
||||||
|
```
|
||||||
|
|
||||||
|
See [Database](/usage/database) for more information on how to configure the database connection.
|
||||||
|
|
||||||
|
### `beforeBuild`
|
||||||
|
The `beforeBuild` property is an async function that is called before the app is built. It allows to modify the app instance that may influence the build process.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { BkndConfig } from "bknd/adapter";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
beforeBuild: async (app: App) => {
|
||||||
|
// do something that has to happen before the app is built
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `onBuilt`
|
||||||
|
The `onBuilt` property is an async function that is called after the app has been built. It allows to hook into the app after it has been built. This is useful for defining event listeners or register custom routes, as both the event manager and the server are recreated during the build process.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { type App, AppEvents } from "bknd";
|
||||||
|
import type { BkndConfig } from "bknd/adapter";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
onBuilt: async (app: App) => {
|
||||||
|
console.log("App built", app.version());
|
||||||
|
|
||||||
|
// registering an event listener
|
||||||
|
app.emgr.onEvent(AppEvents.AppRequest, (event) => {
|
||||||
|
console.log("Request received", event.request.url);
|
||||||
|
})
|
||||||
|
|
||||||
|
// registering a custom route
|
||||||
|
app.server.get("/hello", (c) => c.text("Hello World"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `force` & `id`
|
||||||
|
The `force` property is a boolean that forces the app to be recreated. This is mainly useful for serverless environments where the execution environment is re-used, and you may or may not want to recreate the app on every request.
|
||||||
|
|
||||||
|
The `id` property is the reference in a cache map. You may create multiple instances of apps in the same process by using different ids (e.g. multi tenant applications).
|
||||||
|
|
||||||
|
## Framework & Runtime configuration
|
||||||
|
|
||||||
|
Depending on which framework or runtime you're using to run bknd, the configuration object will extend the `BkndConfig` type with additional properties.
|
||||||
|
|
||||||
|
### `RuntimeBkndConfig`
|
||||||
|
|
||||||
|
[Runtime adapters](/integration/runtime) need additional configuration to serve static assets for the admin UI.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export type RuntimeBkndConfig<Args = any> = BkndConfig<Args> & {
|
||||||
|
// the path to the dist folder to serve static assets for the admin UI
|
||||||
|
distPath?: string;
|
||||||
|
// custom middleware to serve static assets for the admin UI
|
||||||
|
serveStatic?: MiddlewareHandler | [string, MiddlewareHandler];
|
||||||
|
// options for the admin UI
|
||||||
|
adminOptions?: AdminControllerOptions | false;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### `FrameworkBkndConfig`
|
||||||
|
|
||||||
|
[Framework adapters](/integration/framework) may need additional configuration based on the framework's requirements. For example, the `NextjsBkndConfig` type extends the `BkndConfig` type with the following additional properties:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type NextjsEnv = NextApiRequest["env"];
|
||||||
|
export type NextjsBkndConfig<Env = NextjsEnv> = FrameworkBkndConfig<Env> & {
|
||||||
|
cleanRequest?: { searchParams?: string[] };
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Next.js adds the mounted path to the request object, so that the `cleanRequest` property can be used to remove the mounted path from the request URL. See other frameworks for more information on how to configure them.
|
||||||
|
|
||||||
|
|
||||||
|
## Using the configuration file
|
||||||
|
|
||||||
|
The configuration file is automatically picked up if you're using the [CLI](/usage/cli). This allows interacting with your application using the `bknd` command. For example, you can run the following command in the root of your project to start an instance:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx bknd run
|
||||||
|
```
|
||||||
|
|
||||||
|
When serving your application, you need to make sure to import the contents of your configuration file. If you're using Next.js for example, it's recommended to follow these steps:
|
||||||
|
|
||||||
|
1. create a `bknd.config.ts` file in the root of your project which defines the connection to the database, adds event listeners and custom routes.
|
||||||
|
2. create a `bknd.ts` file inside your app folder which exports helper functions to instantiate the bknd instance and retrieve the API.
|
||||||
|
3. create a catch-all route file at `src/api/[[...bknd]]/route.ts` which serves the bknd API.
|
||||||
|
|
||||||
|
This way, your application and the CLI are using the same configuration.
|
||||||
127
docs/extending/events.mdx
Normal file
127
docs/extending/events.mdx
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
---
|
||||||
|
title: Events & Hooks
|
||||||
|
---
|
||||||
|
|
||||||
|
bknd comes with a powerful built-in event system that allows you to hook into the app lifecycle and extend its functionality. You can hook into these events in two ways:
|
||||||
|
|
||||||
|
- `async`: Your listener is not blocking the main execution flow. E.g. on Cloudflare Workers, by default, the `ExecutionContext`'s `waitUntil` method is used so that the listeners runs after the response is sent.
|
||||||
|
- `sync`: Your listener is blocking the main execution flow. This allows to abort the request in your custom conditions. Some events also allow to return a modified event payload.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Don't mistake `async` with JavaScript's `async` keyword. The `async` keyword is used to indicate that the listener is not blocking the main execution flow.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
### Listening to events
|
||||||
|
|
||||||
|
You can listen to events by using the `EventManager` exposed at `app.emgr`. To register a listener, you can use either of these methods:
|
||||||
|
|
||||||
|
- `onEvent`: Register a listener for a specific event with a typed event.
|
||||||
|
- `on`: Register a listener for an event by its slug.
|
||||||
|
- `onAny`: Register a listener for all events.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { createApp, AppEvents } from "bknd";
|
||||||
|
const app = createApp();
|
||||||
|
|
||||||
|
app.emgr.onEvent(AppEvents.AppRequest, async (event) => {
|
||||||
|
// ^? AppRequest
|
||||||
|
console.log("Request received", event.request.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.emgr.on("app-request", async (event) => {
|
||||||
|
console.log("Request received", event.request.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.emgr.onAny(async (event) => {
|
||||||
|
console.log("Event received", event.slug);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
You may want to register your listeners inside [`bknd.config.ts`](/extending/config) to make sure they are registered before the app is built:
|
||||||
|
|
||||||
|
```typescript bknd.config.ts
|
||||||
|
import { AppEvents } from "bknd";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
onBuilt: (app) => {
|
||||||
|
app.emgr.onEvent(AppEvents.AppRequest, async (event) => {
|
||||||
|
console.log("Request received", event.request.url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### Setting a mode
|
||||||
|
By default, listeners are registered as `async` listeners, meaning that the listener is not blocking the main execution flow. You can change this by passing the mode as the third argument:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
app.emgr.onEvent(AppEvents.AppRequest, async (event) => {
|
||||||
|
console.log("Request received", event.request.url);
|
||||||
|
}, { mode: "sync" });
|
||||||
|
```
|
||||||
|
|
||||||
|
This works for all three methods.
|
||||||
|
|
||||||
|
## App Events
|
||||||
|
These events are emitted by the `App` class and are available on the `AppEvents` object.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AppEvents } from "bknd";
|
||||||
|
```
|
||||||
|
|
||||||
|
Available events:
|
||||||
|
|
||||||
|
| Event | Params | Description |
|
||||||
|
|:------|:--------|:------------|
|
||||||
|
| `AppConfigUpdatedEvent` | `{ app: App }` | Emitted when the app configuration is updated |
|
||||||
|
| `AppBuiltEvent` | `{ app: App }` | Emitted when the app is built |
|
||||||
|
| `AppFirstBoot` | `{ app: App }` | Emitted when the app is first booted |
|
||||||
|
| `AppRequest` | `{ app: App, request: Request }` | Emitted when a request is received |
|
||||||
|
| `AppBeforeResponse` | `{ app: App, request: Request, response: Response }` | Emitted before a response is sent |
|
||||||
|
|
||||||
|
## Database Events
|
||||||
|
These events are emitted by the `Database` class and are available on the `DatabaseEvents` object. These are divided by events triggered by the `Mutator` and `Repository` classes.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { DatabaseEvents } from "bknd/data";
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mutator Events
|
||||||
|
These events are emitted during database mutations (insert, update, delete operations).
|
||||||
|
|
||||||
|
| Event | Params | Description |
|
||||||
|
|:------|:--------|:------------|
|
||||||
|
| `MutatorInsertBefore` | `{ entity: Entity, data: EntityData }` | Emitted before inserting a new record. Can modify the data. |
|
||||||
|
| `MutatorInsertAfter` | `{ entity: Entity, data: EntityData, changed: EntityData }` | Emitted after inserting a new record. |
|
||||||
|
| `MutatorUpdateBefore` | `{ entity: Entity, entityId: PrimaryFieldType, data: EntityData }` | Emitted before updating a record. Can modify the data. |
|
||||||
|
| `MutatorUpdateAfter` | `{ entity: Entity, entityId: PrimaryFieldType, data: EntityData, changed: EntityData }` | Emitted after updating a record. |
|
||||||
|
| `MutatorDeleteBefore` | `{ entity: Entity, entityId: PrimaryFieldType }` | Emitted before deleting a record. |
|
||||||
|
| `MutatorDeleteAfter` | `{ entity: Entity, entityId: PrimaryFieldType, data: EntityData }` | Emitted after deleting a record. |
|
||||||
|
|
||||||
|
### Repository Events
|
||||||
|
These events are emitted during database queries (find operations).
|
||||||
|
|
||||||
|
| Event | Params | Description |
|
||||||
|
|:------|:--------|:------------|
|
||||||
|
| `RepositoryFindOneBefore` | `{ entity: Entity, options: RepoQuery }` | Emitted before finding a single record. |
|
||||||
|
| `RepositoryFindOneAfter` | `{ entity: Entity, options: RepoQuery, data: EntityData }` | Emitted after finding a single record. |
|
||||||
|
| `RepositoryFindManyBefore` | `{ entity: Entity, options: RepoQuery }` | Emitted before finding multiple records. |
|
||||||
|
| `RepositoryFindManyAfter` | `{ entity: Entity, options: RepoQuery, data: EntityData }` | Emitted after finding multiple records. |
|
||||||
|
|
||||||
|
## Media Events
|
||||||
|
These events are emitted by the `Storage` class and are available on the `MediaEvents` object.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { MediaEvents } from "bknd/media";
|
||||||
|
```
|
||||||
|
|
||||||
|
| Event | Params | Description |
|
||||||
|
|:------|:--------|:------------|
|
||||||
|
| `FileUploadedEvent` | `{ file: FileBody } & FileUploadPayload` | Emitted when a file is successfully uploaded. Can modify the event data. |
|
||||||
|
| `FileDeletedEvent` | `{ name: string }` | Emitted when a file is deleted. |
|
||||||
|
| `FileAccessEvent` | `{ name: string }` | Emitted when a file is accessed. |
|
||||||
|
|
||||||
|
## Auth Events
|
||||||
|
Coming soon.
|
||||||
@@ -28,14 +28,14 @@ To get started with Astro and bknd you can either install the package manually,
|
|||||||
npx astro add react
|
npx astro add react
|
||||||
```
|
```
|
||||||
|
|
||||||
You also need to make sure to set the output to `hybrid` in your Astro config:
|
You also need to make sure to set the output to `server` in your Astro config:
|
||||||
```js {6}
|
```js {6}
|
||||||
// astro.config.mjs
|
// astro.config.mjs
|
||||||
import { defineConfig } from "astro/config";
|
import { defineConfig } from "astro/config";
|
||||||
import react from "@astrojs/react";
|
import react from "@astrojs/react";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
output: "hybrid",
|
output: "server",
|
||||||
integrations: [react()]
|
integrations: [react()]
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -48,8 +48,58 @@ To get started with Astro and bknd you can either install the package manually,
|
|||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Now create a `bknd.config.ts` file in the root of your project. If you created the project using the CLI starter, this file is already created for you.
|
||||||
|
|
||||||
|
```typescript bknd.config.ts
|
||||||
|
import type { AstroBkndConfig } from "bknd/adapter/astro";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
connection: {
|
||||||
|
url: "file:data.db"
|
||||||
|
},
|
||||||
|
} satisfies AstroBkndConfig;
|
||||||
|
```
|
||||||
|
|
||||||
|
See [bknd.config.ts](/extending/config) for more information on how to configure bknd. The `AstroBkndConfig` type extends the `BkndConfig` type with the following additional properties:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type AstroEnv = NodeJS.ProcessEnv;
|
||||||
|
export type AstroBkndConfig<Env = AstroEnv> = FrameworkBkndConfig<Env>;
|
||||||
|
```
|
||||||
|
|
||||||
## Serve the API
|
## Serve the API
|
||||||
Create a new catch-all route at `src/pages/api/[...api].ts`:
|
Create a helper file to instantiate the bknd instance and retrieve the API, importing the configurationfrom the `bknd.config.ts` file:
|
||||||
|
|
||||||
|
```ts src/bknd.ts
|
||||||
|
import type { AstroGlobal } from "astro";
|
||||||
|
import { getApp as getBkndApp } from "bknd/adapter/astro";
|
||||||
|
import config from "../bknd.config";
|
||||||
|
|
||||||
|
export { config };
|
||||||
|
|
||||||
|
export async function getApp() {
|
||||||
|
return await getBkndApp(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getApi(
|
||||||
|
astro: AstroGlobal,
|
||||||
|
opts?: { mode: "static" } | { mode?: "dynamic"; verify?: boolean },
|
||||||
|
) {
|
||||||
|
const app = await getApp();
|
||||||
|
if (opts?.mode !== "static" && opts?.verify) {
|
||||||
|
const api = app.getApi({ headers: astro.request.headers });
|
||||||
|
await api.verifyAuth();
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.getApi();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a new catch-all route at `src/pages/api/[...api].ts`.
|
||||||
|
|
||||||
```ts src/pages/api/[...api].ts
|
```ts src/pages/api/[...api].ts
|
||||||
import { serve } from "bknd/adapter/astro";
|
import { serve } from "bknd/adapter/astro";
|
||||||
|
|
||||||
@@ -63,6 +113,7 @@ export const ALL = serve({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information about the connection object, refer to the [Database](/usage/database) 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
|
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.
|
under the hood. Refer to the [Astro DB documentation](https://docs.astro.build/en/guides/astro-db/) for more information.
|
||||||
|
|||||||
@@ -22,21 +22,42 @@ To get started with Next.js and bknd you can either install the package manually
|
|||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Now create a `bknd.config.ts` file in the root of your project. If you created the project using the CLI starter, this file is already created for you.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```typescript bknd.config.ts
|
||||||
|
import type { NextjsBkndConfig } from "bknd/adapter/nextjs";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
connection: {
|
||||||
|
url: "file:data.db"
|
||||||
|
},
|
||||||
|
} satisfies NextjsBkndConfig;
|
||||||
|
```
|
||||||
|
|
||||||
|
See [bknd.config.ts](/extending/config) for more information on how to configure bknd. The `NextjsBkndConfig` type extends the `BkndConfig` type with the following additional properties:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type NextjsEnv = NextApiRequest["env"];
|
||||||
|
export type NextjsBkndConfig<Env = NextjsEnv> = FrameworkBkndConfig<Env> & {
|
||||||
|
cleanRequest?: { searchParams?: string[] };
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## Serve the API
|
## Serve the API
|
||||||
Create a helper file to instantiate the bknd instance and retrieve the API:
|
Create a helper file to instantiate the bknd instance and retrieve the API, importing the configurationfrom the `bknd.config.ts` file:
|
||||||
|
|
||||||
```ts src/bknd.ts
|
```ts src/bknd.ts
|
||||||
import { type NextjsBkndConfig, getApp as getBkndApp } from "bknd/adapter/nextjs";
|
import { type NextjsBkndConfig, getApp as getBkndApp } from "bknd/adapter/nextjs";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
|
import config from "../bknd.config";
|
||||||
|
|
||||||
export const config = {
|
export { config };
|
||||||
connection: {
|
|
||||||
url: "file:data.db"
|
|
||||||
},
|
|
||||||
} as const satisfies NextjsBkndConfig;
|
|
||||||
|
|
||||||
export async function getApp() {
|
export async function getApp() {
|
||||||
return await getBkndApp(config);
|
return await getBkndApp(config, process.env);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getApi(opts?: { verify?: boolean }) {
|
export async function getApi(opts?: { verify?: boolean }) {
|
||||||
|
|||||||
@@ -22,21 +22,44 @@ To get started with React Router and bknd you can either install the package man
|
|||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Now create a `bknd.config.ts` file in the root of your project. If you created the project using the CLI starter, this file is already created for you.
|
||||||
|
|
||||||
|
```typescript bknd.config.ts
|
||||||
|
import type { ReactRouterBkndConfig } from "bknd/adapter/react-router";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
connection: {
|
||||||
|
url: "file:data.db"
|
||||||
|
},
|
||||||
|
} satisfies ReactRouterBkndConfig;
|
||||||
|
```
|
||||||
|
|
||||||
|
See [bknd.config.ts](/extending/config) for more information on how to configure bknd. The `ReactRouterBkndConfig` type extends the `BkndConfig` type with the following additional properties:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type ReactRouterEnv = NodeJS.ProcessEnv;
|
||||||
|
type ReactRouterFunctionArgs = {
|
||||||
|
request: Request;
|
||||||
|
};
|
||||||
|
export type ReactRouterBkndConfig<Env = ReactRouterEnv> = FrameworkBkndConfig<Env>;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Serve the API
|
## Serve the API
|
||||||
Create a helper file to instantiate the bknd instance and retrieve the API:
|
Create a helper file to instantiate the bknd instance and retrieve the API, importing the configurationfrom the `bknd.config.ts` file:
|
||||||
|
|
||||||
```ts app/bknd.ts
|
```ts app/bknd.ts
|
||||||
import { type ReactRouterBkndConfig, getApp as getBkndApp } from "bknd/adapter/react-router";
|
import { type ReactRouterBkndConfig, getApp as getBkndApp } from "bknd/adapter/react-router";
|
||||||
|
import config from "../bknd.config";
|
||||||
|
|
||||||
const config = {
|
export { config };
|
||||||
connection: {
|
|
||||||
url: "file:data.db"
|
|
||||||
}
|
|
||||||
} as const satisfies ReactRouterBkndConfig;
|
|
||||||
|
|
||||||
export async function getApp(args?: { request: Request }) {
|
// you may adjust this function depending on your runtime environment.
|
||||||
return await getBkndApp(config, args);
|
// e.g. when deploying to cloudflare workers, you'd want the FunctionArgs to be passed in
|
||||||
|
// to resolve environment variables
|
||||||
|
export async function getApp() {
|
||||||
|
return await getBkndApp(config, process.env as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getApi(
|
export async function getApi(
|
||||||
@@ -60,7 +83,7 @@ Create a new api splat route file at `app/routes/api.$.ts`:
|
|||||||
import { getApp } from "~/bknd";
|
import { getApp } from "~/bknd";
|
||||||
|
|
||||||
const handler = async (args: { request: Request }) => {
|
const handler = async (args: { request: Request }) => {
|
||||||
const app = await getApp(args);
|
const app = await getApp();
|
||||||
return app.fetch(args.request);
|
return app.fetch(args.request);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,25 +16,42 @@ Additionally, install required dependencies:
|
|||||||
npm install @hono/vite-dev-server
|
npm install @hono/vite-dev-server
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Now create a `bknd.config.ts` file in the root of your project.
|
||||||
|
|
||||||
|
```typescript bknd.config.ts
|
||||||
|
import type { ViteBkndConfig } from "bknd/adapter/vite";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
connection: {
|
||||||
|
url: "file:data.db"
|
||||||
|
}
|
||||||
|
} satisfies ViteBkndConfig;
|
||||||
|
```
|
||||||
|
|
||||||
|
See [bknd.config.ts](/extending/config) for more information on how to configure bknd.
|
||||||
|
The `ViteBkndConfig` type extends the `BkndConfig` type with the following additional properties:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export type ViteEnv = NodeJS.ProcessEnv;
|
||||||
|
export type ViteBkndConfig<Env = ViteEnv> = RuntimeBkndConfig<Env> & {};
|
||||||
|
```
|
||||||
|
|
||||||
## Serve the API
|
## Serve the API
|
||||||
To serve the **bknd** API, you first have to create a local server file for you vite environment.
|
To serve the **bknd** API, you first have to create a local server file for you vite environment.
|
||||||
Create a `server.ts` file:
|
Create a `server.ts` file:
|
||||||
```ts
|
|
||||||
|
```typescript
|
||||||
import { serve } from "bknd/adapter/vite";
|
import { serve } from "bknd/adapter/vite";
|
||||||
|
import config from "./bknd.config";
|
||||||
|
|
||||||
// the configuration given is optional
|
export default serve(config)
|
||||||
export default serve({
|
|
||||||
mode: "cached", // that's the default
|
|
||||||
connection: {
|
|
||||||
url: ":memory:"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
```
|
||||||
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.
|
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.
|
This is only useful for when working on the `bknd` repository directly.
|
||||||
|
|
||||||
|
For more information about the connection object, refer to the [Database](/usage/database) guide.
|
||||||
|
|
||||||
Next, adjust your `vite.config.ts` to look like the following:
|
Next, adjust your `vite.config.ts` to look like the following:
|
||||||
```ts
|
```ts
|
||||||
import { devServer } from "bknd/adapter/vite";
|
import { devServer } from "bknd/adapter/vite";
|
||||||
@@ -82,17 +99,22 @@ options you can make use of to adjust it according to your setup.
|
|||||||
There might be cases you want to be sure to be in control over the HTML that is being used.
|
There might be cases you want to be sure to be in control over the HTML that is being used.
|
||||||
`bknd` generates it automatically, but you use your own one as follows:
|
`bknd` generates it automatically, but you use your own one as follows:
|
||||||
|
|
||||||
```ts server.ts
|
```typescript server.ts
|
||||||
import { serve, addViteScript } from "bknd/adapter/vite";
|
import { serve, addViteScript } from "bknd/adapter/vite";
|
||||||
import { readFile } from "node:fs/promises"
|
import { readFile } from "node:fs/promises"
|
||||||
|
import config from "./bknd.config";
|
||||||
|
|
||||||
let html = await readFile("./index.html", "utf-8");
|
let html = await readFile("./index.html", "utf-8");
|
||||||
|
|
||||||
// add vite scripts
|
|
||||||
html = addViteScript(html);
|
|
||||||
|
|
||||||
// then add it as an option
|
// then add it as an option
|
||||||
export default serve({ html })
|
export default serve({
|
||||||
|
...config,
|
||||||
|
adminOptions: {
|
||||||
|
html: addViteScript(html),
|
||||||
|
// optionally, you can change the base path for the admin UI
|
||||||
|
adminBasePath: "/admin"
|
||||||
|
}
|
||||||
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
The vite scripts has to be added manually currently, as adding them automatically with
|
The vite scripts has to be added manually currently, as adding them automatically with
|
||||||
@@ -101,13 +123,18 @@ The vite scripts has to be added manually currently, as adding them automaticall
|
|||||||
### Use a custom entry point
|
### Use a custom entry point
|
||||||
By default, the entry point `/src/main.tsx` is used and should fit most cases. If that's not you,
|
By default, the entry point `/src/main.tsx` is used and should fit most cases. If that's not you,
|
||||||
you can supply a different one like so:
|
you can supply a different one like so:
|
||||||
```ts server.ts
|
|
||||||
|
```typescript server.ts
|
||||||
import { serve } from "bknd/adapter/vite";
|
import { serve } from "bknd/adapter/vite";
|
||||||
|
import config from "./bknd.config";
|
||||||
|
|
||||||
// the configuration given is optional
|
// the configuration given is optional
|
||||||
export default serve({
|
export default serve({
|
||||||
forceDev: {
|
...config,
|
||||||
mainPath: "/src/special.tsx"
|
adminOptions: {
|
||||||
}
|
forceDev: {
|
||||||
|
mainPath: "/src/special.tsx"
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -74,6 +74,10 @@
|
|||||||
"usage/elements"
|
"usage/elements"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"group": "Extending",
|
||||||
|
"pages": ["extending/config", "extending/events"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"group": "Integration",
|
"group": "Integration",
|
||||||
"pages": [
|
"pages": [
|
||||||
|
|||||||
@@ -14,20 +14,21 @@ Here is the output:
|
|||||||
$ npx bknd
|
$ npx bknd
|
||||||
Usage: bknd [options] [command]
|
Usage: bknd [options] [command]
|
||||||
|
|
||||||
⚡ bknd cli v0.10.3-rc.1
|
⚡ bknd cli v0.13.0
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-V, --version output the version number
|
-V, --version output the version number
|
||||||
-h, --help display help for command
|
-h, --help display help for command
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
config [options] get default config
|
user <action> create/update users, or generate a token (auth)
|
||||||
copy-assets [options] copy static assets
|
types [options] generate types
|
||||||
create [options] create a new project
|
|
||||||
debug <subject> debug bknd
|
|
||||||
run [options] run an instance
|
|
||||||
schema [options] get schema
|
schema [options] get schema
|
||||||
user <action> create and update user (auth)
|
run [options] run an instance
|
||||||
|
debug <subject> debug bknd
|
||||||
|
create [options] create a new project
|
||||||
|
copy-assets [options] copy static assets
|
||||||
|
config [options] get default config
|
||||||
help [command] display help for command
|
help [command] display help for command
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -123,4 +124,71 @@ To start an instance with an ephemeral in-memory database, run the following:
|
|||||||
```
|
```
|
||||||
npx bknd run --memory
|
npx bknd run --memory
|
||||||
```
|
```
|
||||||
Keep in mind that the database is not persisted and will be lost when the process is terminated.
|
Keep in mind that the database is not persisted and will be lost when the process is terminated.
|
||||||
|
|
||||||
|
## Generating types (`types`)
|
||||||
|
To see all available `types` options, execute `npx bknd types --help`.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npx bknd types --help
|
||||||
|
Usage: bknd types [options]
|
||||||
|
|
||||||
|
generate types
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-o, --outfile <outfile> output file (default: "bknd-types.d.ts")
|
||||||
|
--no-write do not write to file
|
||||||
|
-h, --help display help for command
|
||||||
|
```
|
||||||
|
|
||||||
|
To generate types for the database, run the following:
|
||||||
|
```
|
||||||
|
npx bknd types
|
||||||
|
```
|
||||||
|
|
||||||
|
This will generate types for your database schema in `bknd-types.d.ts`. The generated file could look like this:
|
||||||
|
|
||||||
|
```typescript bknd-types.d.ts
|
||||||
|
import type { DB } from "bknd/core";
|
||||||
|
import type { Insertable, Selectable, Updateable, Generated } from "kysely";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
type BkndEntity<T extends keyof DB> = Selectable<DB[T]>;
|
||||||
|
type BkndEntityCreate<T extends keyof DB> = Insertable<DB[T]>;
|
||||||
|
type BkndEntityUpdate<T extends keyof DB> = Updateable<DB[T]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Todos {
|
||||||
|
id: Generated<number>;
|
||||||
|
title?: string;
|
||||||
|
done?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Database {
|
||||||
|
todos: Todos;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "bknd/core" {
|
||||||
|
interface DB extends Database {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure to add the generated file in your `tsconfig.json` file:
|
||||||
|
|
||||||
|
```json tsconfig.json
|
||||||
|
{
|
||||||
|
"include": [
|
||||||
|
"bknd-types.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then use the types by importing them from `bknd/core`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { DB } from "bknd/core";
|
||||||
|
|
||||||
|
type Todo = DB["todos"];
|
||||||
|
```
|
||||||
|
|
||||||
|
All bknd methods that involve your database schema will be automatically typed.
|
||||||
@@ -18,7 +18,7 @@ There is also a fourth option, which is running it inside a
|
|||||||
Regardless of the method you choose, at the end all adapters come down to the actual
|
Regardless of the method you choose, at the end all adapters come down to the actual
|
||||||
instantiation of the `App`, which in raw looks like this:
|
instantiation of the `App`, which in raw looks like this:
|
||||||
|
|
||||||
```ts
|
```typescript
|
||||||
import { createApp, type CreateAppConfig } from "bknd";
|
import { createApp, type CreateAppConfig } from "bknd";
|
||||||
|
|
||||||
// create the app
|
// create the app
|
||||||
@@ -38,7 +38,7 @@ implements the `Fetch` API.
|
|||||||
## Configuration (`CreateAppConfig`)
|
## Configuration (`CreateAppConfig`)
|
||||||
The `CreateAppConfig` type is the main configuration object for the `createApp` function. It has
|
The `CreateAppConfig` type is the main configuration object for the `createApp` function. It has
|
||||||
the following properties:
|
the following properties:
|
||||||
```ts
|
```typescript
|
||||||
import type { App, InitialModuleConfigs, ModuleBuildContext } from "bknd";
|
import type { App, InitialModuleConfigs, ModuleBuildContext } from "bknd";
|
||||||
import type { Connection } from "bknd/data";
|
import type { Connection } from "bknd/data";
|
||||||
import type { Config } from "@libsql/client";
|
import type { Config } from "@libsql/client";
|
||||||
|
|||||||
Reference in New Issue
Block a user