Understanding OmniRoute's Database Architecture: Modules, Migrations, and Tables
OmniRoute persists all state in a layered SQLite architecture comprising a singleton core (core.ts), a versioned migration runner (migrationRunner.ts), and 95+ domain-specific modules that manage CRUD operations for entities like providers, combos, and API keys.
The diegosouzapw/OmniRoute repository implements a robust database layer designed to handle routing logic, provider credentials, and usage analytics. Understanding OmniRoute's database architecture reveals how the system balances strict schema evolution with flexible, lazy-loaded domain tables across desktop and browser environments.
The Three-Layer Architecture
OmniRoute splits its data persistence into three tightly coupled components that interact during startup and runtime.
Core Layer: The SQLite Singleton
The Core (src/lib/db/core.ts) establishes the single entry point to the database. It lazily initializes a better-sqlite3 adapter (or falls back to sql.js in browsers) via getDbInstance(), ensuring only one connection handles the storage.sqlite file. The Core also resolves the DATA_DIR (defaulting to ~/.omniroute/), creates the directory on startup, and defines the SCHEMA_SQL constant containing base tables like provider_connections and combos.
Migration Runner: Versioned Evolution
The Migration Runner (src/lib/db/migrationRunner.ts) manages schema changes through numbered SQL files (e.g., 001_initial_schema.sql). It tracks applied versions in the _omniroute_migrations table and enforces safety limits via MAX_PENDING_MIGRATIONS (default 50) to prevent accidental mass migrations.
Domain Modules: Entity-Specific Logic
Domain Modules under src/lib/db/ implement CRUD for specific entities. Each module imports getDbInstance() and calls ensureTable() to lazily create its tables. With over 95 modules covering providers, combos, usage analytics, and compression, this design isolates table logic while sharing the singleton connection.
Core Database Layer: Singleton Management and Base Schema
The Core file serves as the foundation for all database interactions in OmniRoute.
Singleton Creation and Data Directory
The getDbInstance() function in src/lib/db/core.ts acts as the sole gateway to SQLite. It opens the database file and ensures the data directory exists before any queries execute. This singleton pattern prevents connection leaks and ensures thread-safe access across the application's domain modules.
Base Schema and Critical Tables
Lines 89-200 of core.ts define the SCHEMA_SQL constant, which establishes tables required before any domain module runs:
provider_connections– Stores OAuth tokens and API credentials for LLM providerscombos– Contains routing configuration and fallback chainsapi_keys– Manages user-issued keys and their rate-limit policiesproxy_registryandproxy_assignments– Tracks proxy routing statemodel_combo_mappings– Links specific models to combo configurations
The Core also maintains a CRITICAL_DB_TABLES list (lines 98-123) that triggers special backup logic before migrations or shutdowns.
Utility Helpers
The Core exports database-agnostic utilities like rowToCamel() for case conversion, toSnakeCase() for identifiers, and hasTable() for introspection. These functions allow domain modules to remain agnostic to SQLite specifics while maintaining consistent naming conventions.
Migration Runner: Versioned Schema Evolution
Schema changes in OmniRoute follow a strict, file-based versioning system implemented in src/lib/db/migrationRunner.ts.
Migration File Discovery and Tracking
The runner locates SQL files through resolveMigrationsDir() (lines 45-100), which searches multiple paths to support Windows, Linux, and packaged deployments. The getMigrationFiles() function sorts files by numeric prefix and returns version metadata. Before execution, the runner ensures the _omniroute_migrations tracking table exists to record which versions have been applied.
Safety Mechanisms and FTS5 Support
OmniRoute implements several guards to protect data integrity:
- Mass-Migration Guard: If pending migrations exceed
MAX_PENDING_MIGRATIONS(50), the process aborts to prevent accidental wipes on fresh databases - FTS5 Detection: The
supportsFts5()check skips full-text search migrations when the SQLite build lacks the extension - Transactional Safety: Each migration runs inside
db.transaction(() => …), ensuring atomic application—if any statement fails, the entire file rolls back
Automatic Pre-Migration Backups
Before applying pending changes, the runner creates a backup copy in DB_BACKUPS_DIR (referenced in core.ts line 90). This provides recovery options if a migration fails or corrupts the schema.
Domain Modules: Entity-Specific CRUD Operations
OmniRoute organizes persistent logic into discrete modules, each responsible for one entity or logical table family.
Module Structure and Lazy Initialization
Every domain module follows a consistent pattern:
- Import
getDbInstance()from the Core - Define
CREATE TABLE IF NOT EXISTSstatements executed viaensureTable()on first access - Export typed CRUD functions that return plain JavaScript objects (often processed through
rowToCamel())
This lazy initialization allows OmniRoute to start quickly without creating all 95+ tables upfront, while still ensuring schema exists before first use.
Key Domain Modules
The repository includes specialized modules for every major feature:
providers.ts– Managesprovider_connectionswith functions likegetProviders(),upsertProvider(), anddeleteProvider()combos.ts– Handlescombosandmodel_combo_mappingstables, exposinglistCombos(),createCombo(), anddeleteCombo()apiKeys.ts– Controlsapi_keysandapi_key_groupsvialistApiKeys(),createApiKey(), andrevokeApiKey()usageAnalytics.ts– Queriesusage_historyanddomain_cost_historythroughgetDailyUsage()andgetProviderCostRows()compression.ts– Trackscompression_combosandcompression_analyticswithlistCompressionCombos()andrecordCompressionStats()
These modules are consumed by higher-level services including the combo router, MCP toolset, and REST API endpoints.
Database Health and Backup Strategies
OmniRoute includes proactive measures to ensure data integrity across deployments.
Health Checks
The runDbHealthCheck() function (imported in core.ts) validates foreign key integrity and executes sanity queries against known tables. This catches corruption or incomplete migrations before they impact routing logic.
Backup Before Migration
The migration runner automatically copies the current database file to DB_BACKUPS_DIR before executing any pending changes. Combined with the CRITICAL_DB_TABLES tracking, this creates multiple safety nets for production deployments.
Practical Code Examples
Interact with OmniRoute's database layer using these patterns:
// Get the singleton database instance
import { getDbInstance } from "@/src/lib/db/core";
const db = getDbInstance(); // Returns better-sqlite3 or sql.js adapter
// Execute raw SQL against provider connections
const rows = db.prepare("SELECT * FROM provider_connections WHERE is_active = 1").all();
console.log("Active providers:", rows);
// Use high-level domain module APIs
import { listCombos } from "@/src/lib/db/combos";
async function displayRoutingConfig() {
const combos = await listCombos(); // Returns typed, camel-cased objects
console.table(combos);
}
displayRoutingConfig();
// Manually trigger migrations in scripts or maintenance tasks
import { runMigrations } from "@/src/lib/db/migrationRunner";
(async () => {
await runMigrations(); // Applies pending *.sql files transactionally
console.log("Database schema is current");
})();
Summary
- OmniRoute uses a three-layer SQLite architecture: Core singleton (
core.ts), Migration Runner (migrationRunner.ts), and 95+ domain modules (providers.ts,combos.ts, etc.) - The Core manages
getDbInstance(), data directory creation (~/.omniroute/), and base tables includingprovider_connectionsandcombos - Migrations are file-based (numbered
*.sqlfiles) tracked in_omniroute_migrations, with safety guards includingMAX_PENDING_MIGRATIONS(50) and automatic pre-migration backups - Domain modules use lazy initialization, calling
ensureTable()viahasTable()checks only when first accessed, keeping startup fast while ensuring schema compliance - Critical tables defined in
CRITICAL_DB_TABLESreceive special backup treatment before migrations or shutdowns
Frequently Asked Questions
What database does OmniRoute use?
OmniRoute uses SQLite as its primary database, specifically the better-sqlite3 package for Node.js environments. In browser contexts where native SQLite is unavailable, it falls back to sql.js (WebSQL). All persistent state stores in a single file named storage.sqlite located in the DATA_DIR (default ~/.omniroute/).
How does OmniRoute handle schema migrations?
OmniRoute handles schema changes through versioned SQL files stored in the migrations directory. The migrationRunner.ts module reads these files, sorts them by numeric prefix, and applies them transactionally. It tracks completed migrations in the _omniroute_migrations table and includes safety features like the MAX_PENDING_MIGRATIONS guard (default 50) to prevent accidental bulk changes on fresh databases.
What are domain modules in OmniRoute?
Domain modules are the 95+ files under src/lib/db/ that implement CRUD operations for specific entities. Each module (e.g., providers.ts, apiKeys.ts, usageAnalytics.ts) owns its table schema and exposes typed functions like upsertProvider() or revokeApiKey(). They import getDbInstance() from the Core and use lazy initialization to create tables only when first accessed.
How does OmniRoute ensure database safety during migrations?
The system ensures safety through multiple mechanisms: transactional execution (each migration runs in a single transaction that rolls back on failure), automatic pre-migration backups (copies created in DB_BACKUPS_DIR), mass-migration guards (aborting if over 50 pending migrations exist), and FTS5 detection (skipping full-text search migrations on unsupported SQLite builds).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →