AI-powered data query desktop application for building and managing SQL queries across multiple data sources.
- Overview
- Architecture
- Quick Start
- Building for Distribution
- Project Structure
- Key Features
- Data Storage
- Development
- Configuration
- Auto-Updates
- Building Production Apps
- Troubleshooting
- Performance
- Tech Stack Details
- Project Philosophy
- Contributing
- License
- Support
QBox is an Electron desktop application that lets you:
- Connect to PostgreSQL databases and browse schemas with full metadata
- Build SQL queries interactively with AI assistance via chat
- Execute queries across multiple data sources using DuckDB
- Manage data sources including databases, local files, and S3 buckets
- Track query history and iterate with AI-powered refinements
- Desktop: Electron with bundled Python backend
- Frontend: React 18 + TypeScript + TailwindCSS + shadcn/ui
- Backend: Python FastAPI + DuckDB + SQLite
- AI: OpenAI integration for interactive SQL building
- Data: DuckDB for analytics, SQLite for persistence
- Node.js 18+ and npm (or pnpm)
- Python 3.13+
- uv - Python package manager (install)
- macOS, Linux, or Windows
# 1. One-time setup
./setup.sh
# 2. Start backend (Terminal 1)
./run-backend.sh
# 3. Start Electron app (Terminal 2)
cd frontend
npm run electron:devFirst Launch: Configure your OpenAI API key in Settings → it's saved securely in the local database.
That's it! The app opens in an Electron window with DevTools enabled for development.
- Creates Python 3.13 virtual environment using
uv - Installs all backend dependencies (FastAPI, DuckDB, etc.)
- Installs PyInstaller for building distributables
- Installs frontend dependencies (uses
pnpmif available, falls back tonpm) - Creates
.envtemplate if needed
# Build everything and create installers
./build-electron.sh --makeThis builds the Python backend with PyInstaller, builds the React frontend with Vite, and creates platform-specific installers.
# 1. Build backend executable
cd backend
source .venv/bin/activate
python build.py
# 2. Build and package Electron app
cd ../frontend
npm run electron:makeInstallers are created in frontend/out/make/:
- macOS: DMG installer (
*.dmg) - Windows: Squirrel installer (
Setup.exe) - Linux: DEB and RPM packages (
*.deb,*.rpm)
Packaged apps (without installer) are in frontend/out/qbox-{platform}-{arch}/.
Important: Build on the target platform. PyInstaller doesn't support cross-compilation:
- macOS: Build on macOS (Intel or Apple Silicon)
- Windows: Build on Windows
- Linux: Build on Linux
qbox/
├── backend/ # Python FastAPI backend
│ ├── app/
│ │ ├── api/ # API route handlers (thin HTTP layer)
│ │ ├── services/ # Business logic (core functionality)
│ │ ├── models/ # Pydantic schemas
│ │ └── config/ # Settings and configuration
│ ├── build.py # PyInstaller build script
│ └── build.spec # PyInstaller configuration
├── frontend/ # React + Electron frontend
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── services/ # API client (api.ts)
│ │ ├── stores/ # Zustand state management
│ │ └── types/ # TypeScript definitions
│ ├── electron/
│ │ ├── main.ts # Main Electron process
│ │ ├── preload.ts # Preload script (secure IPC)
│ │ └── config.ts # Electron configuration
│ ├── assets/icons/ # Application icons
│ ├── forge.config.ts # Electron Forge configuration
│ └── vite.config.ts # Vite build configuration
├── setup.sh # One-time development setup
├── run-backend.sh # Start backend for development
├── build-electron.sh # Build complete distributable
└── validate-electron-setup.sh # Validate development setup
- PostgreSQL connections with full schema introspection
- Real-time metadata caching (5-minute TTL)
- Browse tables, columns, types, and constraints
- Connection status monitoring
- Extensible architecture for additional database types
- Interactive chat interface for SQL generation
- Iterative query refinement with AI assistance
- Context-aware suggestions based on selected tables
- SQL history tracking per query
- Multi-table joins across different data sources
- PostgreSQL: Full schema browsing and querying
- Local Files: CSV, Parquet, JSON
- S3 Buckets: Cloud storage integration
- Extensible: Architecture supports adding more source types
- In-process analytics engine
- Cross-source queries (join PostgreSQL with CSV, etc.)
- Persistent storage at
~/.qbox/qbox.duckdb - Single writer mode to avoid concurrency issues
- Connections attached with identifiers derived from connection names
- Dark theme design for comfortable use
- Two-page structure: Queries (main) and Connections (management)
- Query Detail: Tabs for SQL editing and table selection
- Tree View: Browse all data sources with metadata
- Real-time filtering: Search tables, show only selected
- Desktop UX: Keyboard shortcuts, native interactions
QBox stores all data locally in ~/.qbox/:
qbox.duckdb: DuckDB database (query results, attached connections)connections.db: SQLite database (connections, queries, selections, chat history)
Schema:
connections: Saved database connectionsqueries: Query definitions with SQL textquery_selections: Table selections per queryquery_chat_history: AI chat messages per query
# Setup
./setup.sh # One-time setup (all dependencies)
./validate-electron-setup.sh # Check setup status
# Development
./run-backend.sh # Start backend server
npm run electron:dev # Start Electron app (from frontend/)
# Building
./build-electron.sh # Package app only
./build-electron.sh --make # Create installers
python backend/build.py # Build backend executable only
npm run electron:build # Package Electron only (from frontend/)
npm run electron:make # Create installers only (from frontend/)
# Frontend only
npm run build # Build React app
npm run lint # Run ESLintIn development mode:
- Backend runs via
./run-backend.shwith hot reload (--reloadflag) - Frontend loads from Vite dev server (http://localhost:5173)
- Backend API available at http://localhost:8080
- DevTools open by default for debugging
- Changes to React code hot reload automatically
- Changes to Python code trigger backend restart
The backend uses:
- FastAPI for REST API
- DuckDB for query execution and analytics
- SQLite for persistent storage
- Pydantic for validation and serialization
- Repository pattern for data access
- uvicorn with single worker (DuckDB constraint)
Code style:
- PEP 8 compliant (Black + Ruff)
- Type hints required for all functions
- Async/await for I/O operations
- Small, single-purpose functions
The frontend uses:
- React 18 functional components with hooks (no classes)
- TypeScript strict mode (no
anytypes) - Zustand for state management
- TailwindCSS + shadcn/ui for styling
- Monaco Editor for SQL editing
Component structure:
- Small, focused components
- Explicit TypeScript types for all props
- Try/catch with user-friendly error messages
- API calls through
services/api.ts
Backend tests use pytest with isolated fixtures. Integration tests use testcontainers to automatically spin up PostgreSQL and LocalStack (S3) containers:
Prerequisites: Docker Desktop must be running for integration tests.
cd backend
# Run all tests (requires Docker for integration tests)
uv run pytest -v
# Run with coverage
uv run pytest --cov=app --cov-report=term-missing
# Run specific test file
uv run pytest tests/integration/test_query_execution.py -v
# Run specific test by name
uv run pytest -k test_execute_query -vTest structure:
tests/conftest.py- Shared fixtures (isolated databases, mocked services, testcontainers)tests/integration/- API and repository tests (connections, queries, S3)
CI/CD runs all tests automatically on PRs and main branch.
Configure your OpenAI API key through the app:
- Open the app
- Go to Settings menu
- Enter your OpenAI API key
- Key is saved in
~/.qbox/connections.db
No .env file needed for the API key - it's managed through the UI.
Create backend/.env for custom backend settings:
# Optional backend configuration
CORS_ORIGINS=http://localhost:5173,http://localhost:8080
LOG_LEVEL=INFOCustom icons go in frontend/assets/icons/:
icon.icns- macOS (512x512)icon.ico- Windows (256x256 with multiple sizes)icon.png- Linux (512x512)
Default Electron icons are used if custom icons aren't provided. See frontend/assets/icons/README.md for details.
The app includes auto-update functionality (production builds only):
- Checks for updates on launch and every 6 hours
- Shows notification when update is available
- Downloads in background
- Prompts to restart when ready
- Configure update server in
frontend/electron/config.ts
Default: Uses GitHub releases. Configure UPDATE_SERVER_URL environment variable for custom servers.
For distribution outside of your organization:
macOS:
- Requires Apple Developer account
- Code signing prevents "unidentified developer" warnings
- Notarization required for Gatekeeper
- Configure in
frontend/forge.config.ts(osxSign,osxNotarize)
Windows:
- Code signing certificate from trusted CA
- Improves SmartScreen reputation
- Use
electron-builderorsigntool
Before distributing:
- Replace placeholder icons with your logo
- Update app metadata in
frontend/forge.config.ts - Configure code signing (macOS/Windows)
- Set up auto-update server or GitHub releases
- Test on all target platforms
- Test auto-update flow
- Create installers with
./build-electron.sh --make - Test installers on clean systems
- Add license file if needed
Virtual environment not found:
./setup.sh # Run setup scriptDependencies missing:
cd backend
uv pip install -e .
uv pip install pyinstallerFrontend dependencies missing:
cd frontend
npm installBackend won't start:
- Check port 8080 is free:
lsof -ti:8080 - Verify venv is activated:
which python(should show.venv) - Check logs in the terminal running
run-backend.sh - Ensure all dependencies installed:
pip list
Electron won't start:
- Ensure backend is running:
curl http://localhost:8080/health - Check DevTools console (View → Toggle Developer Tools)
- Verify frontend dependencies:
cd frontend && npm install - Run validation:
./validate-electron-setup.sh
Backend health check timeout:
- Backend takes 5-10 seconds to start first time (DuckDB initialization)
- Check backend terminal for errors
- Increase timeout in
frontend/electron/config.tsif needed
PyInstaller fails:
- Ensure PyInstaller installed:
pip list | grep pyinstaller - Check for missing hidden imports in
backend/build.spec - Update PyInstaller:
pip install --upgrade pyinstaller - Some packages may not support all platforms
Electron build fails:
- Verify frontend builds:
cd frontend && npm run build - Check for TypeScript errors:
npm run lint - Clean and retry:
rm -rf frontend/out frontend/dist backend/dist - Ensure all dependencies installed:
npm install
DuckDB issues:
- DuckDB files stored at
~/.qbox/qbox.duckdb - If corrupted, delete and restart app
- Ensure only one instance running (single instance lock enforced)
- DuckDB requires single writer (uvicorn runs with
--workers 1)
- First launch: 5-10 seconds (DuckDB initialization)
- Subsequent launches: 2-5 seconds
- App size: 150-250 MB (varies by platform, includes Python runtime)
- Memory usage: 200-400 MB (frontend + backend + Electron)
- Metadata caching: 5-minute TTL reduces redundant queries
- Python 3.13+
- FastAPI for REST API
- DuckDB 0.9+ for analytics
- SQLite for persistence
- uvicorn with standard extras
- Pydantic for validation
- SQLAlchemy for database abstraction
- psycopg for PostgreSQL
- boto3 for S3 integration
- React 18
- TypeScript 5+
- Vite for bundling
- TailwindCSS 4 for styling
- shadcn/ui components
- Zustand for state management
- Monaco Editor for SQL
- Axios for HTTP
- Electron 39+
- Electron Forge for building
- electron-updater for auto-updates
- PyInstaller for backend bundling
- Backend-driven state: Minimize frontend state, persist in backend
- Repository pattern: Clean separation of data access
- Type safety: Full TypeScript and Python type hints
- Electron-first: Designed for desktop from the start
- Cross-platform: Works on macOS, Windows, Linux
- Python: PEP 8, Black formatting, Ruff linting, type hints required
- TypeScript: Strict mode, no
any, explicit types - React: Functional components with hooks only
- API: RESTful design, proper HTTP status codes
- Error handling: User-friendly messages, technical logs server-side
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Test in development mode
- Test the packaged app
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
Windows and macOS releases are digitally signed. See CODE_SIGNING.md for details.
For issues and questions:
- Run
./validate-electron-setup.shto diagnose setup issues - Check the troubleshooting section above
- Open an issue on GitHub
Built with ❤️ using Electron, React, Python, and DuckDB.