---
title: 'Database'
description: 'Choosing the right database configuration'
---

In order to use **bknd**, you need to prepare access information to your database and install the dependencies. Connections to the database are managed using Kysely. Therefore, all [its dialects](https://kysely.dev/docs/dialects) are theoretically supported. 


## 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
{
   "url": ":memory:"
}
```

### SQLite as file
Just like the in-memory option, using a file is just as easy:

```json
{
   "url": "file:<path/to/your/database.db>"
}
```
Please note that using SQLite as a file is only supported in server environments.

### SQLite using LibSQL
Turso offers a SQLite-fork called LibSQL that runs a server around your SQLite database. To
point **bknd** to a local instance of LibSQL, [install Turso's CLI](https://docs.turso.tech/cli/introduction) and run the following command:
```bash
turso dev
```

The command will yield a URL. Use it in the connection object:
```json
{
   "url": "http://localhost:8080"
}
```

### SQLite using LibSQL on Turso
If you want to use LibSQL on Turso, [sign up for a free account](https://turso.tech/), create a database and point your
connection object to your new database:
```json
{
   "url": "libsql://your-database-url.turso.io",
   "authToken": "your-auth-token"
}
```

### Cloudflare D1
Using the [Cloudflare Adapter](/integration/cloudflare), you can choose to use a D1 database binding. To do so, you only need to add a D1 database to your `wrangler.toml` and it'll pick up automatically. 

To manually specify which D1 database to take, you can specify it explicitly:

```ts
import { serve, d1 } from "bknd/adapter/cloudflare";

export default serve<Env>({
  app: ({ env }) => d1({ binding: env.D1_BINDING })
});
```

### PostgreSQL
To use bknd with Postgres, you need to install the `@bknd/postgres` package. You can do so by running the following command:

```bash
npm install @bknd/postgres
```

You can connect to your Postgres database using `pg` or `postgres` dialects. Additionally, you may also define your custom connection.

#### Using `pg`

To establish a connection to your database, you can use any connection options available on the [`pg`](https://node-postgres.com/apis/client) package. 

```js
import { serve } from "bknd/adapter/node";
import { pg } from "@bknd/postgres";

/** @type {import("bknd/adapter/node").NodeBkndConfig} */
const config = {
   connection: pg({
      connectionString:
         "postgresql://user:password@localhost:5432/database",
   }),
};

serve(config);
```

#### Using `postgres`

To establish a connection to your database, you can use any connection options available on the [`postgres`](https://github.com/porsager/postgres) package. 

```js
import { serve } from "bknd/adapter/node";
import { postgresJs } from "@bknd/postgres";

serve({
   connection: postgresJs("postgresql://user:password@localhost:5432/database"),
});
```

#### Using custom connection

Several Postgres hosting providers offer their own clients to connect to their database, e.g. suitable for serverless environments.

Example using `@neondatabase/serverless`:

```js
import { createCustomPostgresConnection } from "@bknd/postgres";
import { NeonDialect } from "kysely-neon";

const connection = createCustomPostgresConnection(NeonDialect)({
   connectionString: process.env.NEON,
});

serve({
   connection: connection,
});
```

Example using `@xata.io/client`:

```js
import { createCustomPostgresConnection } from "@bknd/postgres";
import { XataDialect } from "@xata.io/kysely";
import { buildClient } from "@xata.io/client";

const client = buildClient();
const xata = new client({
   databaseURL: process.env.XATA_URL,
   apiKey: process.env.XATA_API_KEY,
   branch: process.env.XATA_BRANCH,
});

const connection = createCustomPostgresConnection(XataDialect, {
   supports: {
      batching: false,
   },
})({ xata });

serve({
   connection: connection,
});
```


### SQLocal
To use bknd with `sqlocal` for a offline expierence, you need to install the `@bknd/sqlocal` package. You can do so by running the following command:

```bash
npm install @bknd/sqlocal
```

This package uses `sqlocal` under the hood. Consult the [sqlocal documentation](https://sqlocal.dallashoffman.com/guide/setup) for connection options:

```js
import { createApp } from "bknd";
import { SQLocalConnection } from "@bknd/sqlocal";

const app = createApp({
   connection: new SQLocalConnection({
      databasePath: ":localStorage:",
      verbose: true,
   })
});
```

### Custom Connection
Any bknd app instantiation accepts as connection either `undefined`, a connection object like
described above, or an class instance that extends from `Connection`:

```ts
import { createApp } from "bknd";
import { Connection } from "bknd/data";
import { Kysely } from "kysely";

class CustomConnection extends Connection {
   constructor() {
      const kysely = new Kysely(/* ... */);
      super(kysely);
   }
}

const connection = new CustomConnection();

// e.g. and then, create an instance
const app = createApp({ connection })
```

## Initial Structure
To provide an initial database structure, you can pass `initialConfig` to the creation of an app. This will only be used if there isn't an existing configuration found in the database given. Here is a quick example:

<Note>
   The initial structure is only respected if the database is empty! If you made updates, ensure to delete the database first, or perform updates through the Admin UI.
</Note>

```typescript
import { createApp } from "bknd";
import { em, entity, text, number } from "bknd/data";

const schema = em({
   posts: entity("posts", {
      // "id" is automatically added
      title: text().required(),
      slug: text().required(),
      content: text(),
      views: number()
   }),
   comments: entity("comments", {
      content: text()
   })
   
   // relations and indices are defined separately.
   // the first argument are the helper functions, the second the entities.
}, ({ relation, index }, { posts, comments }) => {
   relation(comments).manyToOne(posts);
   // relation as well as index can be chained!
   index(posts).on(["title"]).on(["slug"], true);
});

// to get a type from your schema, use:
type Database = (typeof schema)["DB"];
// type Database = {
//    posts: {
//       id: number;
//       title: string;
//       content: string;
//       views: number;
//    },
//    comments: {
//       id: number;
//       content: string;
//    }
// }

// pass the schema to the app
const app = createApp({
   connection: { /* ... */ },
   initialConfig: {
      data: schema.toJSON()
   }
});
```

Note that we didn't add relational fields directly to the entity, but instead defined them afterwards. That is because the relations are managed outside the entity scope to have an unified expierence for all kinds of relations (e.g. many-to-many).

<Note>
   Defined relations are currently not part of the produced types for the structure. We're working on that, but in the meantime, you can define them manually.
</Note>

### Type completion
To get type completion, there are two options:
1. Use the CLI to [generate the types](/usage/cli#generating-types-types)
2. If you have an initial structure created with the prototype functions, you can extend the `DB` interface with your own schema.

All entity related functions use the types defined in `DB` from `bknd/core`. To get type completion, you can extend that interface with your own schema:

```typescript
import { em } from "bknd/data";
import { Api } from "bknd/client";

const schema = em({ /* ... */ });

type Database = (typeof schema)["DB"];
declare module "bknd/core" {
   interface DB extends Database {}
}

const api = new Api({ /* ... */ });
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](/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` as the first argument.

<Note>
   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.
</Note>

```typescript
import { createApp, type ModuleBuildContext } from "bknd";

const app = createApp({
   connection: { /* ... */ },
   initialConfig: { /* ... */ },
   options: {
      seed: async (ctx: ModuleBuildContext) => {
         await ctx.em.mutator("posts").insertMany([
            { title: "First post", slug: "first-post", content: "..." },
            { title: "Second post", slug: "second-post" }
         ]);
      }
   }
});
```