---
title: "Setup & Modes"
description: "Choose a mode and get bknd running with your app"
icon: Pin
tags: ["documentation"]
---

import { SquareMousePointer, Code, Blend } from 'lucide-react';

bknd supports three modes. Each mode determines how your backend is configured and where that configuration lives.

## Choose your mode

<Cards className="grid-cols-1 sm:grid-cols-2 md:grid-cols-3">
  <Card title="UI-only (default)" href="#ui-only-mode" icon={<SquareMousePointer className="text-fd-primary !size-6" />}>
     Configure visually via the Admin UI. Config is stored in the database.
  </Card>
  <Card title="Code-only" href="#code-only-mode" icon={<Code className="text-fd-primary !size-6" />}>
    Define your schema in code with a Drizzle-like API. Config lives in your codebase.
  </Card>
  <Card title="Hybrid" href="#hybrid-mode" icon={<Blend className="text-fd-primary !size-6" />}>
    Use the Admin UI in development, export to code for production.
  </Card>
</Cards>

**Not sure which to pick?**

- **UI-only** is best for prototyping and small projects. No code needed — configure everything through the dashboard.
- **Code-only** is best for teams, CI/CD, and version-controlled schemas. You define your data structure programmatically.
- **Hybrid** gives you the best of both — visual configuration while developing, locked-down code config in production.

You can always change modes later. Start with UI-only if you're exploring.

---

## UI-only mode

This is the default. Run bknd and configure everything through the Admin UI. No setup code required beyond a database connection.

```typescript title="bknd.config.ts"
import type { BkndConfig } from "bknd";

export default {
  connection: { url: "file:data.db" },
} satisfies BkndConfig;
```

If you want to provide an initial data structure (entities, auth settings, etc.), pass it via `config`. It will only be applied when the database is empty.

```typescript title="bknd.config.ts"
import type { BkndConfig } from "bknd";

export default {
  connection: { url: "file:data.db" },
  config: {
    auth: { enabled: true },
  },
} satisfies BkndConfig;
```

<Callout type="info">
  In UI-only mode, the `config` property is only applied on first boot. After that, all changes are made through the Admin UI.
</Callout>

**Next step:** Pick your [framework or runtime integration](/integration/introduction) to wire bknd into your app.

---

## Code-only mode

Define your data structure programmatically with a Drizzle-like API. The Admin UI becomes read-only for configuration — you still use it to manage data.

```typescript title="bknd.config.ts"
import { type BkndConfig, em, entity, text, boolean } from "bknd";
import { secureRandomString } from "bknd/utils";

const schema = em({
  todos: entity("todos", {
    title: text(),
    done: boolean(),
  }),
});

export default {
  connection: { url: "file:data.db" },
  config: {
    data: schema.toJSON(),
    auth: {
      enabled: true,
      jwt: { secret: secureRandomString(64) },
    },
  },
  options: {
    mode: "code",
  },
} satisfies BkndConfig;
```

Unlike UI-only mode, the `config` is applied on every boot. If you change the schema, you may need to [sync the database](/usage/cli#syncing-the-database-sync).

**Next step:** Pick your [framework or runtime integration](/integration/introduction) to wire bknd into your app.

---

## Hybrid mode

Use the Admin UI to configure your backend while developing. When you're ready to deploy, export the config and run in code mode for production.

```typescript title="bknd.config.ts"
import type { BkndConfig } from "bknd";
import appConfig from "./appconfig.json" with { type: "json" };

export default {
  connection: { url: "file:data.db" },
  config: appConfig,
  options: {
    mode: process.env.NODE_ENV === "development" ? "db" : "code",
    manager: {
      secrets: process.env,
    },
  },
} satisfies BkndConfig;
```

To export your config, secrets, and types, use the CLI or plugins:

| What | CLI Command | Plugin |
|------|-------------|--------|
| Configuration | [`bknd config`](/usage/cli/#getting-the-configuration-config) | [`syncConfig`](/extending/plugins/#syncconfig) |
| Secrets | [`bknd secrets`](/usage/cli/#getting-the-secrets-secrets) | [`syncSecrets`](/extending/plugins/#syncsecrets) |
| Types | [`bknd types`](/usage/cli/#generating-types-types) | [`syncTypes`](/extending/plugins/#synctypes) |

**Next step:** Pick your [framework or runtime integration](/integration/introduction) to wire bknd into your app.

---

## Mode helpers

For code and hybrid modes, bknd provides helper functions that handle syncing, mode switching, and schema validation automatically.

```typescript title="bknd.config.ts (code mode with Bun)"
import { code } from "bknd/modes";
import { type BunBkndConfig, writer } from "bknd/adapter/bun";

export default code<BunBkndConfig>({
  connection: { url: "file:data.db" },
  writer,
  isProduction: Bun.env.NODE_ENV === "production",
  typesFilePath: "bknd-types.d.ts",
});
```

```typescript title="bknd.config.ts (hybrid mode with Bun)"
import { hybrid } from "bknd/modes";
import { type BunBkndConfig, writer, reader } from "bknd/adapter/bun";

export default hybrid<BunBkndConfig>({
  connection: { url: "file:data.db" },
  writer,
  reader,
  secrets: await Bun.file(".env.local").json(),
  isProduction: Bun.env.NODE_ENV === "production",
  typesFilePath: "bknd-types.d.ts",
  configFilePath: "bknd-config.json",
});
```

Mode helpers give you:
- Built-in syncing of config, types, and secrets
- Automatic schema syncing in development
- Automatic mode switching (db → code) in production for hybrid
- Skipped config validation in production for faster boot

---

## Further reading

- [Framework & runtime integrations](/integration/introduction) — wire bknd into Next.js, Astro, Bun, Cloudflare, etc.
- [Database configuration](/usage/database) — choose and configure your SQL database
- [Configuration reference](/extending/config) — full `BkndConfig` API, plugins, events, and lifecycle hooks
