# How Bot Configurations Are Stored and Retrieved in y-gui: A Complete Technical Guide

> Discover how y-gui stores and retrieves bot configurations as JSON in Cloudflare D1 SQLite. Understand the BotD1Repository class and user-specific data handling.

- Repository: [luohy15/y-gui](https://github.com/luohy15/y-gui)
- Tags: how-to-guide
- Published: 2026-03-06

---

**y-gui stores bot configurations as JSON documents in a Cloudflare D1 SQLite database, using the `BotD1Repository` class to handle CRUD operations while isolating data per user through prefixed keys.**

The y-gui system manages AI bot settings through a robust persistence layer that ensures each user maintains independent control over their model configurations. Understanding how bot configurations are stored and retrieved is essential for developers extending the platform or integrating custom models. This guide examines the complete data flow from type definitions to API endpoints based on the actual implementation in the `luohy15/y-gui` repository.

## Bot Configuration Data Structure

### The BotConfig Interface

All bot configurations in y-gui conform to the `BotConfig` interface defined in [`shared/types/index.ts`](https://github.com/luohy15/y-gui/blob/main/shared/types/index.ts). This TypeScript interface establishes the contract for bot settings across the entire application stack.

```typescript
// shared/types/index.ts
export interface BotConfig {
  name: string;
  model: string;
  base_url?: string;
  api_key?: string;
  openrouter_config?: Record<string, any>;
  api_type?: string;
  custom_api_path?: string;
  max_tokens?: number;
}

```

The interface supports various provider configurations through optional fields like `openrouter_config` and `custom_api_path`, allowing the system to accommodate both standard OpenAI-compatible endpoints and specialized routing requirements.

## Database Storage Architecture

### Cloudflare D1 SQLite Table Structure

y-gui persists bot configurations using Cloudflare's D1 database service, which provides SQLite-compatible storage at the edge. The `BotD1Repository` class in [`backend/src/repository/d1/bot-d1-repository.ts`](https://github.com/luohy15/y-gui/blob/main/backend/src/repository/d1/bot-d1-repository.ts) manages all database interactions.

The system stores each bot as a row in the `bot` table with three columns:
- `user_prefix`: A string identifying the user namespace
- `name`: The bot identifier within that namespace
- `json_content`: The stringified JSON representation of the `BotConfig` object

### User Isolation Through Prefix Keys

To ensure multi-tenant isolation, y-gui prepends a user-specific prefix to all database operations. When instantiating `BotD1Repository`, the constructor receives a `userPrefix` parameter that scopes all subsequent queries:

```typescript
// Conceptual usage within the repository
await this.db.prepare(
  "INSERT INTO bot (user_prefix, name, json_content) VALUES (?, ?, ?)"
).bind(this.userPrefix, bot.name, JSON.stringify(bot)).run();

```

This design prevents users from accessing or modifying other users' bot configurations while allowing the same underlying table structure to serve all tenants.

## CRUD Operations with BotD1Repository

### Adding and Updating Bot Configurations

The `BotD1Repository` class provides `addBot()` for creating new configurations and implicit update functionality through SQL `UPDATE` statements. When adding a bot, the method serializes the `BotConfig` object to JSON and inserts it into the D1 table:

```typescript
// backend/src/repository/d1/bot-d1-repository.ts
await this.db.prepare(
  "INSERT INTO bot (user_prefix, name, json_content) VALUES (?, ?, ?)"
).bind(this.userPrefix, bot.name, JSON.stringify(bot)).run();

```

For updates, the repository uses `json_extract` to locate the specific bot by name within the JSON content:

```typescript
await this.db.prepare(
  "UPDATE bot SET json_content = ? WHERE user_prefix = ? AND json_extract(json_content, '$.name') = ?"
).bind(JSON.stringify(bot), this.userPrefix, bot.name).run();

```

### Retrieving Bot Configurations

The `getBots()` method queries all configurations for the current user prefix and deserializes the JSON content back into `BotConfig` objects:

```typescript
const result = await this.db.prepare(
  "SELECT json_content FROM bot WHERE user_prefix = ?"
).bind(this.userPrefix).all();

return result.results.map(row => JSON.parse(row.json_content));

```

### Default Bot Fallback Mechanism

If the database query returns no rows or encounters an error, `BotD1Repository` ensures the UI remains functional by injecting a default bot configuration. The `getDefaultBots()` function returns a hardcoded fallback:

```typescript
// backend/src/repository/d1/bot-d1-repository.ts
function getDefaultBots(): BotConfig[] {
  return [{ name: "default", model: "google/gemini-3-flash-preview" }];
}

```

This guarantees that users always have at least one usable bot even before creating custom configurations.

## API Endpoints for Bot Configuration Access

### GET /api/bots Endpoint

The HTTP layer exposes bot retrieval through the `/api/bots` route defined in [`backend/src/api/bot.ts`](https://github.com/luohy15/y-gui/blob/main/backend/src/api/bot.ts). This endpoint instantiates `BotD1Repository` with the current user's prefix and returns the serialized bot list:

```typescript
// backend/src/api/bot.ts
router.get('/api/bots', async (request, env) => {
  const botRepo = new BotD1Repository(env.CHAT_DB, env, userPrefix);
  const bots = await botRepo.getBots();
  return new Response(JSON.stringify(bots), { headers: { 'Content-Type': 'application/json' } });
});

```

### POST /api/bot Endpoint

Creating or updating bots occurs through the `/api/bot` endpoint. The handler parses the incoming JSON body into a `BotConfig` object and delegates persistence to the repository:

```typescript
// backend/src/api/bot.ts
router.post('/api/bot', async (request, env) => {
  const botConfig = await request.json() as BotConfig;
  const botRepo = new BotD1Repository(env.CHAT_DB, env, userPrefix);
  await botRepo.addBot(botConfig);
  return new Response(JSON.stringify({ success: true }), { headers: { 'Content-Type': 'application/json' } });
});

```

Both routes rely on the `userPrefix` extracted from the request context to ensure proper data isolation.

## Runtime Provider Instantiation

### ProviderFactory Integration

When a chat request requires model interaction, y-gui converts the stored `BotConfig` into an active provider through the `ProviderFactory` class located in [`backend/src/providers/provider-factory.ts`](https://github.com/luohy15/y-gui/blob/main/backend/src/providers/provider-factory.ts). This factory receives the configuration object and returns an appropriate provider instance:

```typescript
// backend/src/providers/provider-factory.ts
const provider = ProviderFactory.createProvider(botConfig);

```

### OpenAIFormatProvider Usage

Currently, the factory instantiates `OpenAIFormatProvider` for all bot configurations. This provider, defined in [`backend/src/providers/openai-format-provider.ts`](https://github.com/luohy15/y-gui/blob/main/backend/src/providers/openai-format-provider.ts), translates the generic `BotConfig` fields into OpenAI-compatible API requests:

```typescript
// Conceptual flow
const provider = new OpenAIFormatProvider(botConfig);
const response = await provider.chat(messages);

```

The provider uses the `base_url`, `api_key`, and `model` fields from the `BotConfig` to construct the HTTP request to the external model endpoint.

## Summary

- **JSON Document Storage**: y-gui stores bot configurations as serialized JSON in a Cloudflare D1 SQLite database, with each row containing a user prefix, bot name, and JSON content.

- **User Isolation**: The `BotD1Repository` class enforces multi-tenancy by binding all queries to a specific `userPrefix`, ensuring users cannot access other users' bot configurations.

- **Default Fallback**: When no bots exist in the database, the system automatically provides a default Gemini 3 configuration to ensure the UI remains functional.

- **Repository Pattern**: All CRUD operations flow through `BotD1Repository` in [`backend/src/repository/d1/bot-d1-repository.ts`](https://github.com/luohy15/y-gui/blob/main/backend/src/repository/d1/bot-d1-repository.ts), which handles SQL generation and JSON serialization.

- **API Exposure**: HTTP endpoints in [`backend/src/api/bot.ts`](https://github.com/luohy15/y-gui/blob/main/backend/src/api/bot.ts) expose bot management through `/api/bots` (retrieval) and `/api/bot` (creation/updates).

- **Runtime Instantiation**: The `ProviderFactory` converts stored `BotConfig` objects into active `OpenAIFormatProvider` instances capable of executing chat requests.

## Frequently Asked Questions

### How does y-gui ensure that users cannot see each other's bot configurations?

y-gui implements user isolation through the `userPrefix` parameter passed to `BotD1Repository`. Every database query includes a `WHERE user_prefix = ?` clause that binds to the specific user's namespace. This design ensures that SQL operations in [`backend/src/repository/d1/bot-d1-repository.ts`](https://github.com/luohy15/y-gui/blob/main/backend/src/repository/d1/bot-d1-repository.ts) only return rows matching the requesting user's prefix, effectively creating a multi-tenant environment within a single D1 table.

### What happens if a user has not created any bot configurations yet?

If the database query in `BotD1Repository.getBots()` returns no rows or encounters an error, the system invokes `getDefaultBots()` to provide a fallback configuration. This function returns a hardcoded array containing a default bot named "default" using the `google/gemini-3-flash-preview` model. This ensures that the frontend UI always has at least one usable bot available, preventing empty-state errors in the chat interface.

### Can y-gui support bot configurations for non-OpenAI API formats?

While the current implementation in [`backend/src/providers/provider-factory.ts`](https://github.com/luohy15/y-gui/blob/main/backend/src/providers/provider-factory.ts) exclusively returns `OpenAIFormatProvider` instances, the architecture supports future expansion. The `BotConfig` interface in [`shared/types/index.ts`](https://github.com/luohy15/y-gui/blob/main/shared/types/index.ts) includes optional fields like `api_type` and `custom_api_path` that could enable factory methods to instantiate different provider classes. Developers could extend `ProviderFactory.createProvider()` to check these fields and return specialized providers for Anthropic, Google, or other API formats while using the same `BotConfig` storage mechanism.

### How are bot configurations validated before being stored in the database?

The current implementation relies on TypeScript interfaces rather than runtime validation schemas. When a bot configuration arrives via the `POST /api/bot` endpoint in [`backend/src/api/bot.ts`](https://github.com/luohy15/y-gui/blob/main/backend/src/api/bot.ts), the handler casts the JSON body directly to `BotConfig` using `await request.json() as BotConfig`. The database layer then serializes this object to JSON without additional validation. This approach assumes the frontend or client provides well-formed data matching the interface defined in [`shared/types/index.ts`](https://github.com/luohy15/y-gui/blob/main/shared/types/index.ts). For production hardening, developers could add JSON Schema validation or Zod parsing at the API boundary before the repository layer persists the configuration.