A Kiroween-themed project that brings Google Spreadsheets back to life as a functional relational database backend.
Universal Backend Solution - Use Google Sheets as a database with Python, REST API, or JavaScript client. No hosting, no bandwidth limits, no setup time.
SheetDB transforms Google Sheets into a powerful relational database with the performance of a traditional backend. It's perfect for:
- Rapid Prototyping - Build MVPs in hours, not days
- π₯ Collaborative Apps - Users can edit data directly in sheets
- π Internal Tools - Admin dashboards without hosting costs
- π Public APIs - Serve data from published sheets (no auth!)
- π Learning Projects - Database concepts without infrastructure
- π 500-1000x Faster - DuckDB caching makes queries blazing fast (vs direct API calls)
- π Relational Queries - Joins, filters, aggregations, subqueries - full SQL power
- π Type Safety - Schema validation and TypeScript support
- π Multiple Interfaces - Python library, REST API, JavaScript client
- π― Framework Agnostic - Works with React, Django, FastAPI, Flask, and more
- π Three Operating Modes - Public (no auth), Authenticated (full power), Hybrid (best of both)
- π Built-in UI - Google Sheets provides the admin interface for free
- β‘ Zero Setup - Public mode works instantly, no credentials needed
pip install sheetdb, you get only the core library. See REPOSITORY_STRUCTURE.md for details.
.
βββ sheetdb/ # Core Python library (pip package)
βββ sheetdb_server/ # REST API server (separate component)
βββ sheetdb-js/ # JavaScript/TypeScript client (npm package)
βββ django_app/ # Monster Hunt Tracker demo
βββ examples/ # Example applications
βββ docs/ # Documentation
βββ tests/ # Test suite
What gets installed with pip install sheetdb:
- β
Core library (
sheetdb/) - β CLI tool
What's separate:
- β REST API server (install FastAPI/uvicorn separately)
- β JavaScript client (separate npm package)
- β Examples (available in source only)
py -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activatepip install -r requirements.txtcp .env.example .env
# Edit .env with your spreadsheet ID and credentials path- Go to Google Cloud Console
- Create a new project or select existing one
- Enable Google Sheets API
- Create a service account
- Download the JSON credentials file
- Share your spreadsheet with the service account email
SheetDB supports three modes to fit different use cases:
Perfect for prototypes, public data, and learning projects. Zero setup required!
from sheetdb import SheetDB
# Just publish your sheet to web and use the URL
db = SheetDB.from_public_url("https://docs.google.com/spreadsheets/d/YOUR_ID/")
db.sync() # Reads from public CSV export
# Query with full SQL power
monsters = db.query("SELECT * FROM monsters WHERE danger_level >= 7")Benefits:
- β No OAuth setup or credentials needed
- β No rate limits on reads (unlimited!)
- β Perfect for prototypes and public data
- β Still get 500x performance boost from caching
How to enable:
- In Google Sheets: File β Share β Publish to web
- Select "Entire Document" and "Web page"
- Copy the URL and use it with SheetDB
Full CRUD operations with Google Sheets API. Best for private data and production apps.
from sheetdb import SheetDB
# Use service account credentials
db = SheetDB(
credentials="credentials.json",
spreadsheet_id="YOUR_SPREADSHEET_ID",
cache_mode="aggressive" # Load once, query many times
)
db.sync() # One-time sync from Sheets API
# Unlimited fast queries (cached in DuckDB)
for i in range(1000):
data = db.query("SELECT * FROM monsters") # ~0.001s each!
# Write operations
db.insert('monsters', {'name': 'Vampire', 'danger_level': 8})
db.update('monsters', id=1, data={'danger_level': 9})
db.delete('monsters', id=1)Benefits:
- β Full CRUD operations
- β Private data support
- β 500x faster reads via caching
- β Controlled write access
Setup:
- Create Google Cloud project
- Enable Google Sheets API
- Create service account and download JSON key
- Share spreadsheet with service account email
Combine public reads (unlimited) with authenticated writes (controlled). Recommended for production!
from sheetdb import SheetDB
# Public reads + authenticated writes
db = SheetDB(
sheet_url="https://docs.google.com/spreadsheets/d/YOUR_ID/",
credentials="credentials.json",
mode="hybrid",
cache_mode="smart",
cache_ttl=300 # Auto-refresh every 5 minutes
)
# Unlimited fast reads from public URL
data = db.query("SELECT * FROM monsters")
# Controlled writes via API
db.insert('monsters', {'name': 'Vampire', 'danger_level': 8})Benefits:
- β Unlimited reads (no rate limits)
- β Controlled writes (authenticated)
- β Best performance
- β Auto-sync for collaborative editing
SheetDB uses a layered architecture that works with any framework:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Application Layer (React, Django, FastAPI, etc.) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Interface Layer (JS Client, REST API, ORM) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Core SheetDB Library (Unified API) β
β ββ Sheets Gateway (API + Public URLs) β
β ββ DuckDB Engine (Relational Cache) β
β ββ Schema Config (Validation) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Data Sources (Google Sheets, Public URLs, Forms) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key Principles:
- Google Sheets = Source of truth for all data
- DuckDB = In-memory relational cache (500x faster queries)
- Schema Config = Type safety and validation
- Framework Agnostic = Works with any Python/JS framework
For a detailed explanation of how SheetDB works, see ARCHITECTURE.md.
The fastest way to get started - no authentication required!
from sheetdb import SheetDB
# 1. Publish your Google Sheet to web (File β Share β Publish to web)
# 2. Use the URL with SheetDB
db = SheetDB.from_public_url("https://docs.google.com/spreadsheets/d/YOUR_ID/")
# 3. Sync data (one-time load into cache)
db.sync()
# 4. Query with full SQL power
monsters = db.query("""
SELECT m.name, m.danger_level, COUNT(h.id) as hunt_count
FROM monsters m
LEFT JOIN hunts h ON m.id = h.monster_id
GROUP BY m.id, m.name, m.danger_level
ORDER BY hunt_count DESC
""")
# 5. That's it! No credentials, no setup, just works.from sheetdb import SheetDB
# Public mode (no auth)
db = SheetDB.from_public_url("https://docs.google.com/spreadsheets/d/YOUR_ID/")
# Authenticated mode (full CRUD)
db = SheetDB(
credentials="credentials.json",
spreadsheet_id="YOUR_ID",
cache_mode="aggressive" # Default: load once, query many times
)
# Hybrid mode (public reads + auth writes)
db = SheetDB(
sheet_url="https://docs.google.com/spreadsheets/d/YOUR_ID/",
credentials="credentials.json",
mode="hybrid"
)
# Cache modes
db = SheetDB(..., cache_mode="aggressive") # Load once (default)
db = SheetDB(..., cache_mode="smart", cache_ttl=300) # Auto-refresh every 5 min
db = SheetDB(..., cache_mode="lazy") # Cache on-demand
db = SheetDB(..., cache_mode="none") # No cache (like Retrosheet)
# Operations
db.sync() # Refresh cache from sheets
data = db.query("SELECT * FROM monsters WHERE danger_level > 5")
db.insert('monsters', {'name': 'Vampire', 'danger_level': 8})
db.update('monsters', id=1, data={'danger_level': 9})
db.delete('monsters', id=1)Start a universal backend that any frontend can use:
# Start the server
cd sheetdb_server
python server.py
# API is now available at http://localhost:8000
# Docs at http://localhost:8000/docsAPI Endpoints:
# List all tables
GET /api/tables
# Query rows with filters
GET /api/tables/monsters/rows?danger_level__gte=7&limit=10&order_by=name
# Create row
POST /api/tables/monsters/rows
{"name": "Vampire", "danger_level": 8}
# Update row
PUT /api/tables/monsters/rows/1
{"danger_level": 9}
# Delete row
DELETE /api/tables/monsters/rows/1
# Trigger sync
POST /api/sync
# Check sync status
GET /api/sync/statusUse SheetDB from any JavaScript/TypeScript frontend:
npm install sheetdb-jsVanilla JavaScript:
import { SheetDB } from "sheetdb-js";
const db = new SheetDB("http://localhost:8000/api");
// Fluent query API
const monsters = await db
.table("monsters")
.filter({ danger_level__gte: 7 })
.orderBy("name")
.limit(10)
.get();
// CRUD operations
await db.table("monsters").create({ name: "Vampire", danger_level: 8 });
await db.table("monsters").update(1, { danger_level: 9 });
await db.table("monsters").delete(1);
// Trigger sync
await db.sync();React Hooks:
import { SheetDBProvider, useQuery, useMutation } from "sheetdb-js/react";
function App() {
return (
<SheetDBProvider apiUrl="http://localhost:8000/api">
<MonsterList />
</SheetDBProvider>
);
}
function MonsterList() {
const { data, loading, error, refetch } = useQuery("monsters", {
filter: { danger_level__gte: 7 },
orderBy: "name",
});
const { mutate: createMonster } = useMutation("monsters", "create");
if (loading) return <div>Loading monsters...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Dangerous Monsters ({data.length})</h1>
{data.map((monster) => (
<div key={monster.id}>
{monster.name} - Danger Level: {monster.danger_level}
</div>
))}
<button
onClick={() => createMonster({ name: "Vampire", danger_level: 8 })}
>
Add Monster
</button>
<button onClick={refetch}>Refresh</button>
</div>
);
}SheetDB includes a command-line tool for quick setup and management:
# Install SheetDB with CLI
pip install -e .
# Initialize from public URL (no auth required)
sheetdb init --url "https://docs.google.com/spreadsheets/d/YOUR_ID/"
# Initialize with authentication
sheetdb init --credentials creds.json --spreadsheet-id YOUR_ID
# Sync data
sheetdb sync --url "https://docs.google.com/spreadsheets/d/YOUR_ID/"
# Generate schema
sheetdb schema generate --credentials creds.json --spreadsheet-id YOUR_ID
# Get help
sheetdb --helpFor complete CLI documentation, see docs/CLI_USAGE.md.
SheetDB provides a Django-like ORM for working with your spreadsheet data. The ORM infrastructure is provided by sheetdb, while model definitions live in your application code (e.g., Django app).
Important: SheetDB does not support Django migrations. The schema is defined in sheets_schema.yaml and Google Sheets is the source of truth. Models simply map to existing sheets - they don't create or modify the sheet structure.
from sheetdb import Model
class Monster(Model):
class Meta:
table_name = "monsters"
primary_key = "id"
class Hunter(Model):
class Meta:
table_name = "hunters"
primary_key = "id"
class Hunt(Model):
class Meta:
table_name = "hunts"
primary_key = "id"
def get_monster(self):
"""Get related monster."""
if self.monster_id:
return Monster.objects.get(id=self.monster_id)
return Nonefrom sheetdb import (
SheetsGateway,
SchemaConfig,
DuckDBEngine,
configure_orm
)
# Initialize components
gateway = SheetsGateway('credentials.json', 'your-spreadsheet-id')
schema = SchemaConfig('.kiro/specs/sheetdb/sheets_schema.yaml')
engine = DuckDBEngine(":memory:")
# Configure ORM
configure_orm(engine, gateway, schema)
# Sync data from sheets
engine.refresh_all_tables(gateway, schema)# Get all records
monsters = Monster.objects.all()
# Filter records
dangerous = Monster.objects.filter(danger_level__gte=7)
# Get single record
dracula = Monster.objects.get(id=1)
# Chain filters
forest_monsters = Monster.objects.filter(habitat="Forest").filter(danger_level__gte=5)
# Order results
ordered = Monster.objects.all().order_by('-danger_level')
# Count records
count = Monster.objects.count()field: Exact matchfield__gte: Greater than or equalfield__lte: Less than or equalfield__contains: String contains
# Create new record
monster = Monster.create(
id=10,
name='Vampire',
danger_level=8,
habitat='Crypt'
)
# Update existing record
monster.danger_level = 9
monster.save()# Get related objects
hunt = Hunt.objects.get(id=1)
monster = hunt.get_monster()
hunter = hunt.get_hunter()See examples/orm_example.py for a complete working example.
SheetDB uses a different approach than traditional Django ORMs:
- Create your Google Sheet with the desired structure (columns, headers)
- Define the schema in
sheets_schema.yamlto map sheets to tables - Define models in your Django app that match the schema
- Validate using
python validate_schema.py
To add a new table:
- Create a new sheet in Google Sheets
- Add the table definition to
sheets_schema.yaml - Create a model class in your Django app
- Run validation to ensure everything is configured correctly
Note: Django's makemigrations and migrate commands are not used with SheetDB. The schema is managed through the YAML configuration file and Google Sheets itself.
For detailed Django setup instructions, see docs/django_setup.md.
This project follows spec-driven development. See .kiro/specs/sheetdb/ for:
requirements.md- Feature requirementsdesign.md- Architecture and design decisionstasks.md- Implementation task list
Before deploying or running the application, validate your schema configuration:
python validate_schema.py <config_path> <credentials_path>Example:
python validate_schema.py .kiro/specs/sheetdb/sheets_schema.yaml path/to/credentials.jsonThe validation script checks:
- All referenced sheets exist in the spreadsheet
- All referenced columns exist in sheets
- Primary key columns exist
- Foreign key columns exist in both source and target tables
Exit codes:
0: Validation successful1: Validation failed with errors2: Script error (missing files, invalid arguments, etc.)
# Run all tests
pytest
# Run with coverage
pytest --cov=sheetdb
# Run property-based tests
pytest -k property- Architecture Overview - System design and components
- Vision & Roadmap - Project goals and future plans
- Unified API - Core SheetDB class documentation
- ORM Design Guide - When to use ORM vs raw SQL
- CLI Usage - Command-line tool guide
- Django Setup - Django integration guide
- Phase 2 Complete - Public mode and forms
- Phase 3 Complete - REST API server
- Phase 4 Complete - JavaScript client
- JavaScript Examples - JS client examples
examples/sheetdb_example.py- Core SheetDB usageexamples/orm_example.py- ORM usageexamples/cli_example.sh- CLI commandsexamples/rest_api_example.sh- REST API usageexamples/forms_writer_example.py- Forms integrationexamples/js-client-vanilla.html- Vanilla JavaScript exampleexamples/js-client-react.jsx- React example
The Problem with Direct API Calls:
Retrosheet and similar libraries query Google Sheets API directly for every request. This is slow and hits rate limits quickly.
# Retrosheet approach (direct API calls)
import retrosheet
sheet = retrosheet.Retrosheet("YOUR_ID", "credentials.json")
# Every query hits the API (slow!)
for i in range(100):
data = sheet.fetch("monsters") # ~0.5s each = 50 seconds total!SheetDB's Solution: Smart Caching
SheetDB syncs data once into DuckDB, then queries run in-memory at blazing speed:
# SheetDB approach (cached queries)
from sheetdb import SheetDB
db = SheetDB.from_public_url("https://docs.google.com/spreadsheets/d/YOUR_ID/")
db.sync() # One-time sync: ~2 seconds
# Unlimited fast queries (in-memory!)
for i in range(100):
data = db.query("SELECT * FROM monsters") # ~0.001s each = 0.1 seconds total!
# Result: 500x faster! (50s β 0.1s)Feature Comparison:
| Feature | SheetDB | Retrosheet | Traditional DB |
|---|---|---|---|
| Query Speed | β‘ 0.001s (cached) | π 0.5s (API call) | β‘ 0.001s |
| Relational Queries | β Full SQL (joins, aggregations) | β No joins | β Full SQL |
| Rate Limits | β Unlimited reads (public mode) | β 100 req/100s | β Unlimited |
| Setup Time | β 5 minutes (public mode) | β Hours (hosting) | |
| Hosting Cost | β $0 (Google's infrastructure) | β $0 | β $5-50/month |
| Built-in UI | β Google Sheets | β Google Sheets | β Build your own |
| Type Safety | β Schema validation | β No validation | β Schema enforcement |
| Multiple Interfaces | β Python, REST API, JS | β Python only | |
| Collaborative Editing | β Real-time in Sheets | β Real-time in Sheets | β Build your own |
When to Use Each:
- SheetDB: Prototypes, internal tools, collaborative apps, public APIs
- Retrosheet: Simple scripts, one-off data fetches
- Traditional DB: High-scale production apps (1M+ rows, complex transactions)
Advantages of SheetDB:
- β No Hosting - Leverage Google's infrastructure (free!)
- β Unlimited Bandwidth - Google handles all traffic
- β Built-in Admin UI - Google Sheets is your admin panel
- β Collaboration - Multiple users can edit simultaneously
- β Version History - Built-in audit trail
- β Zero Setup - Public mode works instantly
- β Familiar Interface - Everyone knows spreadsheets
When Traditional DB is Better:
- β Very large datasets (1M+ rows)
- β Complex transactions (ACID guarantees)
- β High write throughput (1000s writes/sec)
- β Advanced features (triggers, stored procedures)
Advantages of SheetDB:
- β No Vendor Lock-in - Your data lives in Google Sheets
- β Familiar Interface - Spreadsheets vs custom admin panels
- β No Learning Curve - Everyone knows how to use sheets
- β Free Tier - Google Sheets is free for most use cases
- β Offline Editing - Edit sheets offline, sync later
- β Relational Queries - Full SQL power with joins
When Firebase/Supabase is Better:
- β Real-time subscriptions (live updates)
- β Authentication/authorization (built-in)
- β File storage (built-in)
- β Serverless functions (built-in)
- π Rapid Prototyping - Build MVPs in hours
- π₯ Internal Tools - Admin dashboards, data entry forms
- π Public APIs - Serve data from published sheets
- π Learning Projects - Database concepts without infrastructure
- π€ Collaborative Apps - Users edit data directly in sheets
- π Data Analysis - SQL queries on spreadsheet data
- β High-scale production (1M+ rows)
- β Real-time gaming (sub-100ms latency)
- β Financial transactions (ACID guarantees)
- β High write throughput (1000s writes/sec)
MIT