mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-02 16:16:02 +00:00
246 lines
6.8 KiB
Plaintext
246 lines
6.8 KiB
Plaintext
---
|
|
title: "React Router"
|
|
description: "Run bknd inside React Router"
|
|
tags: ["documentation"]
|
|
---
|
|
|
|
## Installation
|
|
|
|
To get started with React Router and bknd you can either install the package manually, and follow the descriptions below, or use the CLI starter:
|
|
|
|
### CLI Starter
|
|
|
|
Create a new React Router CLI starter project by running the following command:
|
|
|
|
```sh
|
|
npx bknd create -i react-router
|
|
```
|
|
|
|
### Manual
|
|
|
|
Create a new React Router project by following the [official guide](https://reactrouter.com/start/framework/installation), and then install bknd as a dependency:
|
|
|
|
<Tabs groupId='package-manager' persist items={[ 'npm', 'pnpm', 'yarn', 'bun']}>
|
|
|
|
```bash tab="npm"
|
|
npm install bknd
|
|
```
|
|
|
|
```bash tab="pnpm"
|
|
pnpm install bknd
|
|
```
|
|
|
|
```bash tab="yarn"
|
|
yarn add bknd
|
|
```
|
|
|
|
```bash tab="bun"
|
|
bun add bknd
|
|
```
|
|
|
|
</Tabs>
|
|
|
|
## Configuration
|
|
|
|
<Callout type="warning">
|
|
When run with Node.js, a version of 22 (LTS) or higher is required. Please
|
|
verify your version by running `node -v`, and
|
|
[upgrade](https://nodejs.org/en/download/) if necessary.
|
|
</Callout>
|
|
|
|
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 title="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
|
|
|
|
### Helper Functions (Optional)
|
|
|
|
For convenience, you can create a helper file to instantiate the bknd instance and retrieve the API. This is optional but recommended as it simplifies usage throughout your app. The examples below assume you've created this helper, but you can adjust the approach according to your needs.
|
|
|
|
```ts title="app/bknd.ts"
|
|
import {
|
|
type ReactRouterBkndConfig,
|
|
getApp as getBkndApp,
|
|
} from "bknd/adapter/react-router";
|
|
import config from "../bknd.config";
|
|
|
|
export { config };
|
|
|
|
// you may adjust this function depending on your runtime environment.
|
|
// 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(
|
|
args?: { request: Request },
|
|
opts?: { verify?: boolean },
|
|
) {
|
|
const app = await getApp();
|
|
if (opts?.verify) {
|
|
const api = app.getApi({ headers: args?.request.headers });
|
|
await api.verifyAuth();
|
|
return api;
|
|
}
|
|
|
|
return app.getApi();
|
|
}
|
|
```
|
|
|
|
For more information about the connection object, refer to the [Database](/usage/database) guide.
|
|
|
|
### API Route
|
|
|
|
Create a catch-all route file at `app/routes/api.$.ts` that forwards requests to bknd:
|
|
|
|
```ts title="app/routes/api.$.ts"
|
|
import { getApp } from "~/bknd";
|
|
|
|
const handler = async (args: { request: Request }) => {
|
|
const app = await getApp();
|
|
return app.fetch(args.request);
|
|
};
|
|
|
|
export const loader = handler;
|
|
export const action = handler;
|
|
```
|
|
|
|
If you're using [`@react-router/fs-routes`](https://reactrouter.com/how-to/file-route-conventions), this file will automatically be picked up as a route.
|
|
|
|
If you're manually defining routes in [`app/routes.ts`](https://reactrouter.com/api/framework-conventions/routes.ts), reference this file in your configuration:
|
|
|
|
```ts title="app/routes.ts"
|
|
import { type RouteConfig, route } from "@react-router/dev/routes";
|
|
|
|
export default [
|
|
// your other routes...
|
|
route("api/*", "./routes/api.$.ts"),
|
|
] satisfies RouteConfig;
|
|
```
|
|
|
|
## Enabling the Admin UI
|
|
|
|
Create a route file at `app/routes/admin.$.tsx` to enable the bknd Admin UI for managing your data, schema, and users:
|
|
|
|
```tsx title="app/routes/admin.$.tsx"
|
|
import { lazy, Suspense, useSyncExternalStore } from "react";
|
|
import { type LoaderFunctionArgs, useLoaderData } from "react-router";
|
|
import { getApi } from "~/bknd";
|
|
|
|
const Admin = lazy(() =>
|
|
import("bknd/ui").then((mod) => ({ default: mod.Admin })),
|
|
);
|
|
import "bknd/dist/styles.css";
|
|
|
|
export const loader = async (args: LoaderFunctionArgs) => {
|
|
const api = await getApi(args, { verify: true });
|
|
return {
|
|
user: api.getUser(),
|
|
};
|
|
};
|
|
|
|
export default function AdminPage() {
|
|
const { user } = useLoaderData<typeof loader>();
|
|
// derived from https://github.com/sergiodxa/remix-utils
|
|
// @ts-ignore
|
|
const hydrated = useSyncExternalStore(
|
|
() => {},
|
|
() => true,
|
|
() => false,
|
|
);
|
|
if (!hydrated) return null;
|
|
|
|
return (
|
|
<Suspense>
|
|
<Admin
|
|
withProvider={{ user }}
|
|
config={{ basepath: "/admin", logo_return_path: "/../" }}
|
|
/>
|
|
</Suspense>
|
|
);
|
|
}
|
|
```
|
|
|
|
If you're using [`@react-router/fs-routes`](https://reactrouter.com/how-to/file-route-conventions), this file will automatically be picked up as a route.
|
|
|
|
If you're manually defining routes in `app/routes.ts`, reference this file in your configuration:
|
|
|
|
```ts title="app/routes.ts"
|
|
import { type RouteConfig, route } from "@react-router/dev/routes";
|
|
|
|
export default [
|
|
// your other routes...
|
|
route("admin/*", "./routes/admin.$.tsx"),
|
|
] satisfies RouteConfig;
|
|
```
|
|
|
|
## Example usage of the API
|
|
|
|
You can use the `getApi` helper function we've already set up to fetch and mutate:
|
|
|
|
```tsx title="app/routes/_index.tsx"
|
|
import { useLoaderData, type LoaderFunctionArgs } from "react-router";
|
|
import { getApi } from "~/bknd";
|
|
|
|
export const loader = async (args: LoaderFunctionArgs) => {
|
|
// use authentication from request
|
|
const api = await getApi(args, { verify: true });
|
|
const { data } = await api.data.readMany("todos");
|
|
return { data, user: api.getUser() };
|
|
};
|
|
|
|
export default function Index() {
|
|
const { data, user } = useLoaderData<typeof loader>();
|
|
|
|
return (
|
|
<div>
|
|
<h1>Data</h1>
|
|
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
<h1>User</h1>
|
|
<pre>{JSON.stringify(user, null, 2)}</pre>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Using React Hooks (Optional)
|
|
|
|
If you want to use bknd's client-side React hooks (like `useEntityQuery`, `useAuth`, etc.), wrap your app in the `ClientProvider` component. This is typically done in `app/root.tsx`:
|
|
|
|
```tsx title="app/root.tsx"
|
|
// other imports
|
|
import { ClientProvider } from "bknd/client";
|
|
|
|
// ...
|
|
export default function App() {
|
|
return (
|
|
<ClientProvider>
|
|
<Outlet />
|
|
</ClientProvider>
|
|
);
|
|
}
|
|
// ...
|
|
```
|
|
|
|
The `ClientProvider` automatically uses the same origin for API requests, which works perfectly when bknd is served from your React Router app. For more details on using React hooks, see the [React SDK documentation](/usage/react). |