Of course. Here is a detailed report on the necessary changes to create a database-backed backend for your PocketLLM project, including API design and a database schema.
PocketLLM is a cross-platform AI chat application built with Flutter. Its core strength lies in its ability to interact with various Large Language Models (LLMs), either by running them locally on a device (via Ollama in Termux) or by connecting to external providers like OpenAI, Anthropic, and others.
Currently, the application relies heavily on local storage (shared_preferences, sqflite, flutter_secure_storage) for managing user data, chat history, and model configurations. While this is effective for a standalone, offline-first application, moving to a centralized backend will enable cross-device synchronization, more robust data management, and enhanced security.
This report outlines the steps and design considerations for migrating PocketLLM to a client-server architecture with a Flutter frontend and a NestJS/Node.js backend.
The current FastAPI backend ships with a consolidated model catalogue pipeline:
- Provider records are read from Supabase and decrypted per user request.
ProvidersService.get_provider_modelsverifies an API key exists for each provider before instantiating SDK clients—environment fallbacks are intentionally disabled.ProviderModelCataloguefans out to OpenAI, Groq, OpenRouter, and ImageRouter using their official Python SDKs, applying caching and structured logging.- Responses are wrapped in
ProviderModelsResponse, exposing the aggregatedmodelslist along with helpfulmessage,configured_providers, andmissing_providersfields so the Flutter UI can prompt users to add credentials when necessary.
Both GET /v1/models and GET /v1/providers/{provider}/models rely on this workflow, guaranteeing consistent behaviour regardless of whether the UI requests a specific provider or the entire catalogue.
The new architecture will consist of:
- Flutter Frontend: The existing application will be refactored to act as a client. It will handle the UI and user interactions, but all data persistence and business logic will be offloaded to the backend via API calls.
- NestJS/Node.js Backend: A new backend server will be created to manage:
- User authentication and sessions.
- Database interactions (storing and retrieving all application data).
- Proxying requests to third-party LLM providers. This is crucial for security, as it keeps provider API keys off the client devices.
- Database: A relational database (like PostgreSQL or MySQL) will be used to store all application data.
Below is a proposed set of RESTful API endpoints that the backend will need to expose.
Handles user registration, login, and session management.
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/register |
Creates a new user account. |
POST |
/auth/login |
Authenticates a user and returns a session token (e.g., JWT). |
POST |
/auth/logout |
Invalidates the user's session token. |
GET |
/auth/me |
Retrieves the profile of the currently authenticated user. |
Manages user profile information.
| Method | Endpoint | Description |
|---|---|---|
GET |
/users/:id |
Retrieves a user's public profile. |
PATCH |
/users/me |
Updates the authenticated user's profile information. |
Manages the user's saved LLM provider configurations.
| Method | Endpoint | Description |
|---|---|---|
POST |
/model-configs |
Creates a new model configuration for the user. |
GET |
/model-configs |
Retrieves all model configurations for the user. |
GET |
/model-configs/:id |
Retrieves a single model configuration. |
PATCH |
/model-configs/:id |
Updates an existing model configuration. |
DELETE |
/model-configs/:id |
Deletes a model configuration. |
Manages chat conversations and messages.
| Method | Endpoint | Description |
|---|---|---|
POST |
/chats |
Creates a new chat conversation. |
GET |
/chats |
Retrieves all of the user's chat conversations (metadata only). |
GET |
/chats/:id |
Retrieves a single conversation, including all of its messages. |
POST |
/chats/:id/messages |
Sends a new message to a conversation. The backend will then proxy this request to the appropriate LLM provider. |
DELETE |
/chats/:id |
Deletes a conversation and all of its messages. |
Proxies requests to the ImageRouter service.
| Method | Endpoint | Description |
|---|---|---|
POST |
/images/generations |
Generates an image based on a prompt, proxying the request to ImageRouter. |
A relational database is recommended. The following schema outlines the necessary tables, columns, and relationships.
Stores user account and profile information.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
UUID | PRIMARY KEY | Unique identifier for the user. |
email |
VARCHAR(255) | UNIQUE, NOT NULL | User's email address. |
password_hash |
VARCHAR(255) | NOT NULL | Hashed password. |
full_name |
VARCHAR(255) | User's full name. | |
username |
VARCHAR(255) | UNIQUE | User's chosen username. |
bio |
TEXT | A short biography of the user. | |
date_of_birth |
DATE | User's date of birth. | |
profession |
VARCHAR(255) | User's profession. | |
avatar_url |
VARCHAR(255) | URL to the user's profile picture. | |
created_at |
TIMESTAMP | NOT NULL | Timestamp of account creation. |
updated_at |
TIMESTAMP | NOT NULL | Timestamp of the last profile update. |
Stores user-defined configurations for different LLM providers.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
UUID | PRIMARY KEY | Unique identifier for the configuration. |
user_id |
UUID | FOREIGN KEY (users.id) | The user who owns this configuration. |
name |
VARCHAR(255) | NOT NULL | A user-friendly name for the configuration. |
provider |
VARCHAR(50) | NOT NULL | The LLM provider (e.g., 'openai', 'ollama'). |
base_url |
VARCHAR(255) | NOT NULL | The base URL for the provider's API. |
api_key_encrypted |
TEXT | The encrypted API key for the provider. | |
model |
VARCHAR(255) | NOT NULL | The specific model to be used (e.g., 'gpt-4'). |
system_prompt |
TEXT | A custom system prompt for the model. | |
temperature |
FLOAT | The temperature setting for the model. | |
created_at |
TIMESTAMP | NOT NULL | Timestamp of configuration creation. |
updated_at |
TIMESTAMP | NOT NULL | Timestamp of the last configuration update. |
Stores metadata for each chat conversation.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
UUID | PRIMARY KEY | Unique identifier for the chat. |
user_id |
UUID | FOREIGN KEY (users.id) | The user who owns this chat. |
model_config_id |
UUID | FOREIGN KEY (model_configs.id) | The model configuration used for this chat. |
title |
VARCHAR(255) | NOT NULL | A title for the chat, generated from the first message. |
created_at |
TIMESTAMP | NOT NULL | Timestamp of chat creation. |
updated_at |
TIMESTAMP | NOT NULL | Timestamp of the last message in the chat. |
Stores individual messages within each chat.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
UUID | PRIMARY KEY | Unique identifier for the message. |
chat_id |
UUID | FOREIGN KEY (chats.id) | The chat this message belongs to. |
content |
TEXT | NOT NULL | The text content of the message. |
role |
VARCHAR(50) | NOT NULL | The role of the message sender ('user' or 'assistant'). |
timestamp |
TIMESTAMP | NOT NULL | Timestamp of when the message was created. |
metadata |
JSONB | Additional data, such as sources, errors, or token counts. |
To integrate with the new backend, the following services and components in the Flutter app will need to be refactored:
-
lib/services/local_db_service.dartandlib/services/auth_service.dart: These will be replaced with a newApiServicethat handles all communication with the backend. Authentication logic will involve storing a session token (JWT) and sending it with each request. -
lib/services/chat_history_service.dart: Instead of reading from/writing toshared_preferences, this service will make API calls to the/chatsendpoints on the backend. -
lib/services/model_service.dart: This service will be updated to fetch, create, update, and delete model configurations via the/model-configsAPI endpoints. -
lib/services/chat_service.dart: The logic for communicating with different LLM providers will be removed. Instead, it will make a single API call to the backend's/chats/:id/messagesendpoint, sending the user's message and letting the backend handle the rest. -
lib/services/image_router_service.dart: This will be refactored to call the/images/generationsendpoint on your backend instead of directly calling the ImageRouter API. -
UI Components (
lib/pages/andlib/component/): Pages likeProfileSettingsPage,ModelSettingsPage, andChatInterfacewill need to be updated to use the newApiServicefor all data operations. State management will need to handle loading, error, and success states for API calls.
By following this plan, you can successfully migrate PocketLLM to a more scalable, secure, and feature-rich client-server architecture.