DEV Community

Cover image for Introducing SheetDB: Turn Google Sheets into your database
Zero | One
Zero | One Subscriber

Posted on • Edited on

Introducing SheetDB: Turn Google Sheets into your database

Introduction

This year, I participated in Kiroween, and for this hackathon, I built SheetDB—a project that transforms Google Sheets into a fully operational relational database with SQL capabilities, multiple tables, relationships, and performance that's 500-1000x faster than making direct API calls to Google Sheets to achieve a similar goal.

You might wonder: why use Google Sheets as a database when there are fully-featured database solutions like PostgreSQL, MySQL, or managed services like Supabase, Firebase, and Appwrite? The answer is simple: because it is fun to hack something like this and a lot useful at the same time and more importantly, because I can

The Benefits of Using Google Sheets as a Database

  1. Zero Hosting Costs - Leverage Google's infrastructure for free, with near-unlimited bandwidth and reliability
  2. Built-in Admin Interface - Google Sheets provides a familiar, powerful UI that everyone already knows how to use
  3. Instant Setup - No database installation, no server configuration, no DevOps—just publish your sheet and start querying
  4. Real-time Collaboration - Multiple users can edit data simultaneously with Google's built-in collaboration features
  5. Version History - Every change is tracked automatically, providing a built-in audit trail
  6. Unlimited Writes via Forms - Use Google Forms for data collection without consuming API quota

The SheetDB Ecosystem

SheetDB is now a comprehensive ecosystem with three main components:

  1. SheetDB Core - Python library and CLI tool providing the core database functionality
  2. sheetdb-js - JavaScript/TypeScript client with React hooks for frontend integration
  3. sheetdb-server - FastAPI REST API server that exposes SheetDB to any language or client

The Inspiration

There have been several attempts at using Google Sheets as a database for simple MVPs and CRUD applications. The most notable is Retrosheet, which elegantly turns Google Sheets into a single-table database and which was the main inspiration to this project(all credits to theapache64). It's brilliant for quick prototypes, but as I had more ambitious project in my mind on top of this which addresses following issues:

  • No real database queries - Couldn't write SQL or perform complex filtering
  • Single table only - All data had to live in one sheet with no relationships
  • No relational data - Impossible to model users → posts, products → orders, or any other relationships
  • Slow performance - Every query hits the Google Sheets API directly (~500ms per query)
  • Rate limits - Limited to 100 requests per 100 seconds
  • Limited flexibility - Rigid architecture with no room for customisation

I needed something more powerful, more flexible, but still free and easy to deploy.

That's when I decided to build SheetDB to fill exactly those gaps.

What SheetDB Does: A Complete Solution

SheetDB transforms Google Sheets into a full-featured relational database with all the power of traditional databases, but zero hosting costs.

Core Features

🗄️ True Relational Database

  • Multiple tables with relationships - Define foreign keys and join data across sheets
  • Full SQL support - JOINs, aggregations, subqueries, WHERE clauses, GROUP BY, ORDER BY
  • Schema validation - Type checking and constraint enforcement
  • Primary keys and referential integrity - Maintain data consistency
# Complex joins across multiple tables
hunts = db.query("""
    SELECT h.hunt_date, m.name as monster, hu.name as hunter, h.success
    FROM hunts h
    JOIN monsters m ON h.monster_id = m.id
    JOIN hunters hu ON h.hunter_id = hu.id
    WHERE h.success = true
    ORDER BY h.hunt_date DESC
""")
Enter fullscreen mode Exit fullscreen mode

⚡ 500x Performance Boost

The secret sauce: DuckDB caching layer

  • Sync data once from Google Sheets into in-memory DuckDB
  • All queries run against the cache (~1ms vs ~500ms)
  • Smart sync strategies: aggressive, lazy, scheduled, or manual

Performance Comparison:

Operation Direct API (Retrosheet) SheetDB (Cached) Speedup
Single query 500ms 1ms 500x
100 queries 50 seconds 0.1 seconds 500x
Join query Not possible 1ms
# One sync, unlimited fast queries
db.sync()  # ~2 seconds (one-time cost)

for i in range(1000):
    data = db.query('SELECT * FROM monsters')  # ~1ms each!
# Total: ~2 seconds vs Retrosheet's ~500 seconds
Enter fullscreen mode Exit fullscreen mode

🎯 Three Flexible Operating Modes

1. Public Mode (No Authentication)

  • Read from published Google Sheets without any credentials
  • Unlimited reads from public CSV exports
  • Optional unlimited writes via Google Forms
  • Perfect for prototypes and public data

2. Authenticated Mode (Full CRUD)

  • Full create, read, update, delete operations
  • Private data support with service account authentication
  • Complete control over all operations

3. Hybrid Mode (Best of Both) ⚡ Recommended

  • Public reads (unlimited, fast)
  • Authenticated writes (controlled)
  • Optimal performance for production apps
# Public mode - zero setup!
db = SheetDB.from_public_url("https://docs.google.com/spreadsheets/d/YOUR_ID/")

# Authenticated mode - full control
db = SheetDB(credentials="creds.json", spreadsheet_id="YOUR_ID")

# Hybrid mode - best of both worlds
db = SheetDB(
    sheet_url="https://...",
    credentials="creds.json",
    mode="hybrid"
)
Enter fullscreen mode Exit fullscreen mode

📝 Multiple Read/Write Options

For Reads:

  • Direct SQL queries with DuckDB
  • Django-like ORM for Python
  • REST API for any frontend
  • JavaScript client with React hooks

For Writes:

  • Direct API writes (authenticated mode)
  • Google Forms integration (unlimited writes, no API quota!)
  • Automated form creation via Forms API
# Unlimited writes via Google Forms (no API quota!)
form_url = db.enable_forms_writer('monsters', auto_create=True)

for i in range(1000):  # Write 1000 records - no limits!
    db.insert_via_form('monsters', {
        'name': f'Monster_{i}',
        'danger_level': (i % 10) + 1
    })
Enter fullscreen mode Exit fullscreen mode

🤖 Everything Automated

  • Auto-generate schemas from existing sheets
  • Auto-create sheets from schema definitions
  • Auto-create forms for data entry
  • Auto-sync data on schedule
  • Auto-validate against schema
# Generate schema from existing sheets
sheetdb schema generate --credentials creds.json --spreadsheet-id YOUR_ID

# Create sheets from schema
sheetdb sheets create --schema schema.yaml

# Create forms for all tables
sheetdb forms init --schema schema.yaml
Enter fullscreen mode Exit fullscreen mode

🌐 Framework Agnostic

Works with any Python or JavaScript framework:

  • ✅ Django (with ORM adapter)
  • ✅ FastAPI (REST API server)
  • ✅ Flask
  • ✅ React (with hooks)
  • ✅ Vue
  • ✅ Angular
  • ✅ Next.js
  • ✅ Vanilla JS/Python

How I Built It: Architecture and Technical Decisions

Building SheetDB required careful architectural decisions to balance simplicity, performance, and flexibility.

Core Architecture

┌─────────────────────────────────────────────────────┐
│  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)   │
└─────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Key Technical Decisions

1. DuckDB as Cache Layer

The Problem: Google Sheets API is slow (~500ms per query) and rate-limited (100 requests per 100 seconds).

The Solution: Cache data in DuckDB, an embedded analytical database.

Why DuckDB?

  • In-memory - No disk I/O, blazing fast queries
  • Columnar storage - Optimized for analytical queries
  • Vectorized execution - SIMD operations for speed
  • Full SQL support - JOINs, aggregations, subqueries
  • Embedded - No separate server process

Implementation:

class DuckDBEngine:
    def __init__(self):
        self.conn = duckdb.connect(':memory:')

    def load_table(self, table_name, data, schema):
        # Create table from sheet data
        self.conn.execute(f"CREATE OR REPLACE TABLE {table_name} AS SELECT * FROM data")

    def execute_sql(self, query, params=None):
        # Execute with parameter binding (SQL injection safe)
        return self.conn.execute(query, params).fetchall()
Enter fullscreen mode Exit fullscreen mode

Result: 500-1000x performance improvement!

2. Three Operating Modes

The Problem: Different use cases need different authentication strategies.

The Solution: Support three modes with seamless switching.

Public Mode Implementation:

class PublicSheetsReader:
    def read_sheet(self, sheet_url, gid=0):
        # Construct CSV export URL
        csv_url = f"https://docs.google.com/spreadsheets/d/{id}/export?format=csv&gid={gid}"

        # Fetch and parse CSV (no authentication!)
        response = urllib.request.urlopen(csv_url)
        return list(csv.DictReader(io.StringIO(response.read().decode('utf-8'))))
Enter fullscreen mode Exit fullscreen mode

Benefit: Zero setup for prototypes, full control for production.

3. Forms API for Unlimited Writes

The Problem: Google Sheets API has strict rate limits for writes.

The Solution: Use Google Forms API to create forms that write to sheets without consuming API quota.

Implementation:

class FormsAPIGateway:
    def create_form(self, title, spreadsheet_id, sheet_name):
        # Create form via Forms API
        form = self.forms_service.forms().create(body={'info': {'title': title}}).execute()

        # Link to spreadsheet
        self.link_to_spreadsheet(form['formId'], spreadsheet_id, sheet_name)

        return form['formId']

    def submit_response(self, form_id, data, entry_mapping):
        # Submit via HTTP POST (no API quota!)
        form_data = {entry_mapping[field]: value for field, value in data.items()}
        response = requests.post(submit_url, data=form_data)
        return response.url
Enter fullscreen mode Exit fullscreen mode

Benefit: Unlimited writes for high-volume data collection!

4. Schema-First Design

The Problem: Google Sheets are untyped and unstructured.

The Solution: YAML schema configuration with validation.

Schema Example:

tables:
  - name: monsters
    sheet_name: Monsters
    primary_key: id
    columns:
      - name: id
        type: integer
      - name: name
        type: string
      - name: danger_level
        type: integer

relationships:
  - name: hunt_monster
    from_table: hunts
    from_column: monster_id
    to_table: monsters
    to_column: id
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Type safety and validation
  • Automatic code generation
  • Clear documentation
  • Enables relational queries

5. Framework Agnostic Core

The Problem: Different projects use different frameworks.

The Solution: Build a clean core library with adapters for specific frameworks.

Architecture:

  • Core Library - Framework-independent SheetDB class
  • Django Adapter - ORM layer mimicking Django models
  • REST API - FastAPI server for universal access
  • JS Client - npm package for frontend frameworks

Result: Works with any framework, no vendor lock-in!

Challenges I Faced and How I Solved Them

Challenge 1: Google API Rate Limits

Problem: The Sheets API has strict rate limits (100 requests per 100 seconds).

Solution: DuckDB caching. Sync once, query unlimited times. This turned a limitation into our biggest performance advantage.

Impact: 500x performance improvement and no rate limit issues for reads.

Challenge 2: Relational Data in Flat Sheets

Problem: Google Sheets are inherently flat, but we needed relationships.

Solution: Built a schema system that maps sheets to tables and defines foreign keys, then DuckDB handles the joins in-memory.

Implementation:

# Schema defines relationships
relationships:
  - from_table: hunts
    from_column: monster_id
    to_table: monsters
    to_column: id

# DuckDB executes joins
SELECT h.*, m.name as monster_name
FROM hunts h
JOIN monsters m ON h.monster_id = m.id
Enter fullscreen mode Exit fullscreen mode

Challenge 3: Type Safety Without a Real Database

Problem: Sheets store everything as strings.

Solution: Implemented automatic type inference and validation, converting strings to proper types (integers, dates, booleans) based on the schema.

def infer_type(value):
    try:
        return int(value)
    except ValueError:
        try:
            return float(value)
        except ValueError:
            if value.lower() in ('true', 'false'):
                return value.lower() == 'true'
            return value
Enter fullscreen mode Exit fullscreen mode

Challenge 4: Multiple Framework Support

Problem: Supporting Django, FastAPI, React, and vanilla JS meant building for many different environments.

Solution: Used the adapter pattern to keep framework-specific code separate from the core library.

Result: Clean architecture with well-defined interfaces.

Challenge 5: Authentication Complexity

Problem: Google's authentication can be tricky and intimidating for beginners.

Solution: Simplified it with three modes: public (no auth), authenticated (service account), and hybrid. Each mode has clear setup instructions and examples.

Documentation: Created comprehensive guides with step-by-step instructions for each mode.

Challenge 6: Schema Management

Problem: Manually writing schemas is tedious and error-prone.

Solution: Built three ways to create schemas:

  1. Interactive CLI - Step-by-step guided creation
  2. Auto-generation - Infer from existing sheets
  3. Manual YAML - Full control for advanced users
# Interactive creation
sheetdb schema create

# Auto-generation
sheetdb schema generate --credentials creds.json --spreadsheet-id YOUR_ID
Enter fullscreen mode Exit fullscreen mode

Challenge 7: Forms API Integration

Problem: The Forms API is powerful but complex.

Solution: Abstracted it into simple functions: create a form, link it to a sheet, and get unlimited writes. All automated.

# One line to enable unlimited writes
form_url = db.enable_forms_writer('monsters', auto_create=True)
Enter fullscreen mode Exit fullscreen mode

Accomplishments I'm Proud Of

🎯 True Relational Database

We didn't just add features—we fundamentally changed what's possible. Multiple tables, foreign keys, SQL joins, aggregations. It's a real database now.

🆓 Still Completely Free

Despite all the advanced features, SheetDB remains free to use. No hosting costs, no bandwidth limits, no hidden fees. Google's infrastructure handles everything.

🤖 Automation That Actually Works

From schema generation to form creation, everything can be automated. This saves hours of manual setup and reduces errors.

📚 Comprehensive Documentation

I built extensive documentation with examples, tutorials, troubleshooting guides, and API references. The Django app and React examples show real-world usage.(check repo link)

🎨 Beautiful Demo Apps

The Monster Hunt Tracker (Django) showcases the full stack with a Kiro dark mode + Halloween theme. It's functional and fun. (check repo link)

🌐 Framework Flexibility

Works with Python, JavaScript, Django, FastAPI, React—whatever you're building with. Not locked into any specific framework.

📊 Production-Ready

Type safety, validation, error handling, logging, testing. SheetDB isn't just a prototype—it's ready for real applications.

What I Learned

Technical Lessons

1. Caching is Everything
The DuckDB layer transformed performance. Sometimes the best solution isn't faster APIs, it's not calling them at all.

2. Constraints Breed Creativity
Google Sheets' limitations forced us to think differently. The result is more innovative than a traditional database wrapper.

3. Simplicity Wins
The three operating modes (public, authenticated, hybrid) cover 99% of use cases. We resisted adding complexity.

4. Documentation Matters
Good docs with examples are as important as good code. We invested heavily in guides, tutorials, and working demos.

5. Framework Agnostic is Hard
Supporting multiple frameworks requires careful architecture. The adapter pattern and clean interfaces were essential.

How I Used Kiro: Spec-Driven Development Meets Vibe Coding

Building SheetDB with Kiro combined two approaches: spec-driven development for core architecture and vibe coding for rapid iteration—achieving ~20x faster development.

Spec-Driven Development

For critical features, I used Kiro's structured workflow with formal requirements (.kiro/specs/sheetdb/requirements.md), detailed design documents with correctness properties, and phased task lists. This provided clear direction, testable acceptance criteria, and maintainable architecture for the database engine, ORM layer, and API contracts.

Vibe Coding

For UI/UX and documentation, I used conversational iteration with Kiro:

  • Theme Design: "Make it like Kiro dark mode with Halloween emojis" → Complete CSS in 20 minutes (vs 4-6 hours)
  • Documentation: "Cover authentication for 3 Google APIs" → Production-ready guides in 30 minutes (vs 2-3 days)
  • Debugging: Screenshot of error → Complete fix with automation scripts in 10 minutes (vs 1-2 hours)

The Key Insight

Use spec-driven when correctness matters most (architecture, core logic). Use vibe coding when speed matters most (styling, docs, automation). Together: production-ready system in ~2 hours vs ~4-5 days traditional development.

What's Next for SheetDB

Short-term (Next 3 Months)

1. Incremental Sync and some improvements

  • Only update changed data instead of full reloads
  • Track modifications and sync deltas
  • Free analytics for data via Google Forms
  • Finish and police the product, write detailed documentation and host it for users.
  • expose sheetdb server to be used for any other project.

2. More Examples

  • Flask app example
  • Full React App example using sheetdb-js
  • Show SheetDB working with every major framework

Medium-term (6 Months)

1. Multi-Spreadsheet Support

  • Combine data from multiple Google Sheets
  • Join across spreadsheets
  • Aggregate data from multiple sources

2. Advanced and more complete SQL Relationships

  • Many-to-many relationships
  • Polymorphic associations
  • Self-referential relationships

3. Schema Migrations

  • Version control for schemas
  • Add columns, change types
  • Migrate data safely

4. Offline Support

  • Local-first architecture with sync
  • Work offline, sync when connected
  • Conflict resolution

Long-term (1 Year+)

1. Visual Schema Builder

  • Web UI for designing schemas
  • No YAML required
  • Drag-and-drop table relationships

2. GraphQL API

  • Alternative to REST API
  • More flexible queries from frontends
  • Better for complex data fetching

Try SheetDB Today!

Ready to build your next project with zero hosting costs and database power?

Quick Start (5 Minutes)

# Install
pip install git+https://github.com/yourusername/sheetdb.git

# Initialize from public URL (no auth required!)
sheetdb init --url "https://docs.google.com/spreadsheets/d/YOUR_ID/"

# Start querying!
Enter fullscreen mode Exit fullscreen mode
from sheetdb import SheetDB

# Connect to your sheet
db = SheetDB.from_public_url("https://docs.google.com/spreadsheets/d/YOUR_ID/")
db.sync()

# Query with SQL
monsters = db.query("SELECT * FROM monsters WHERE danger_level > 7")

# That's it! 🎉
Enter fullscreen mode Exit fullscreen mode

Links

Join the Community

  • ⭐ Star the project on GitHub
  • 🐛 Report bugs and request features
  • 💡 Share your projects built with SheetDB
  • 📝 Contribute to documentation
  • 🤝 Help other developers

Built with 🎃 Kiroween spirit

Because the best solutions come from working around limitations, not throwing money at them.

Top comments (0)