---
title: "Choosing a backend"
description: "Choose persistence and attachment storage independently, with explicit factories bound to verified identity."
---

`createChatHandler` separates conversation persistence (`ChatStore`) from
attachment storage (`StorageAdapter`). Both are factories called after
authentication with the verified `userId`.

```ts
type ChatStoreFactory = (userId: string) => ChatStore;
type StorageAdapterFactory = (userId: string) => StorageAdapter;
```

## Options

| Need | Factory |
| --- | --- |
| Self-hosted Postgres conversations | `createDrizzleChatStore()` |
| Private Supabase attachments | `createSupabaseStorage()` |
| Hosted conversations | `createHostedChatStore({ apiKey })` |
| Hosted attachments | `createHostedStorage({ apiKey })` |
| Custom database/object store | Your own user-bound factory |

Persistence is currently required; there is no silent default store. Storage is
optional and uploads are disabled when it is absent.

```ts
export const { GET, POST, DELETE } = createChatHandler({
  getUserId: getChatUserId,
  model,
  store: createDrizzleChatStore(),
  storage: createSupabaseStorage(),
});
```

Mix the axes deliberately: Drizzle plus Supabase, hosted store plus custom
storage, or fully custom implementations. All custom stores/adapters must uphold
the ownership and private-attachment invariants.

**[Drizzle / Postgres](/docs/backends/drizzle)**

Current chat schema and connection options.

**[Supabase storage](/docs/backends/supabase)**

Private bucket and signed URLs.

**[Hosted](/docs/backends/hosted)**

Server-side hosted clients and config fallbacks.

**[Custom](/docs/backends/custom)**

Implement the user-bound contracts.
