Installation

To get started with Remix and bknd you can either install the package manually, and follow the descriptions below, or use the CLI starter:

Create a new Remix CLI starter project by running the following command:

npx bknd create -i remix

Serve the API

Since Remix doesn’t support middleware yet, we need a helper file to initialize the App to import from. Create a new file at app/bknd.ts:

app/bknd.ts
import { type RemixBkndConfig, getApp as getBkndApp } from "bknd/adapter/remix";

const config = {
   connection: {
      url: "file:data.db"
   }
} as const satisfies RemixBkndConfig;

export async function getApp(args?: { request: Request }) {
   return await getBkndApp(config, args);
}

export async function getApi(args?: { request: Request }) {
   const app = await getApp(args);
   if (args) {
      const api = app.getApi(args.request);
      await api.verifyAuth();
      return api;
   }

   return app.getApi();
}

Create a new api splat route file at app/routes/api.$.ts:

app/routes/api.$.ts
import { getApp } from "~/bknd";

const handler = async (args: { request: Request }) => {
   const app = await getApp(args);
   return app.fetch(args.request);
};

export const loader = handler;
export const action = handler;

For more information about the connection object, refer to the Database guide.

Now make sure that you wrap your root layout with the ClientProvider so that all components share the same context. Also add the user context to both the Outlet and the provider:

app/root.tsx
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
import { useLoaderData, Outlet } from "@remix-run/react";
import { ClientProvider } from "bknd/client";
import { getApi } from "~/bknd";

export function Layout(props) {
   // nothing to change here, just for orientation
   return (
      <html>{/* ... */}</html>
   );
}

export const loader = async (args: LoaderFunctionArgs) => {
   const api = await getApi(args);
   return {
      user: api.getUser()
   };
};

export default function App() {
   const data = useLoaderData<typeof loader>();
   return (
      <ClientProvider user={data.user}>
         <Outlet context={data} />
      </ClientProvider>
   );
}

Enabling the Admin UI

Create a new splat route file at app/routes/admin.$.tsx:

app/routes/admin.$.tsx
import { adminPage } from "bknd/adapter/remix";
import "bknd/dist/styles.css";

export default adminPage({
   config: {
      basepath: "/admin",
      logo_return_path: "/../",
      color_scheme: "system"
   }
});

Example usage of the API

Since the API has already been constructed in the root layout, you can now use it in any page:

app/routes/_index.tsx
import { useLoaderData } from "@remix-run/react";
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
import { getApi } from "~/bknd";

export const loader = async (args: LoaderFunctionArgs) => {
   // use authentication from request
   const api = await getApi(args);
   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>
   );
}