SQL Power, No Vendor Lock-In, Self-Hosted
Firebase is Google's Backend-as-a-Service platform built around a proprietary NoSQL document database (Firestore). HeliosDB Nano gives you a full relational SQL database with the same BaaS conveniences — auth, REST API, realtime — without vendor lock-in or cloud dependency.
| Feature | Firebase | HeliosDB Nano |
|---|---|---|
| Data Model | NoSQL (Firestore documents) | Relational SQL + JSONB |
| Query Language | Proprietary SDK queries | Standard SQL (PostgreSQL dialect) |
| Hosting | Google Cloud only | Self-hosted (any server) |
| Auth | Firebase Auth (email, OAuth, phone) | Built-in (email, OAuth, JWT) |
| Realtime | Firestore onSnapshot | WebSocket (postgres_changes) |
| Server Logic | Cloud Functions (Node.js) | PL/pgSQL stored procedures |
| REST API | Firebase REST API | PostgREST-compatible (auto-generated) |
| File Storage | Cloud Storage for Firebase | In-memory (disk persistence planned) |
| Vector Search | Firebase Extensions (preview) | Native HNSW (production-ready) |
| Joins | Not supported (denormalize) | Full SQL JOINs |
| Aggregation | Limited (count, sum, avg) | Full SQL (GROUP BY, HAVING, window functions) |
| Transactions | Limited (document-level) | Full ACID (multi-table) |
| Branching | N/A | Built-in (zero-cost, COW) |
| Time Travel | N/A | Built-in (AS OF TIMESTAMP) |
| Encryption | Google-managed keys | AES-256-GCM TDE (your keys) |
| Open Source | SDKs only (backend proprietary) | Source available (SSPL-1.0) |
| Vendor Lock-In | High (Google Cloud) | None (self-hosted) |
| Offline Support | Firestore offline cache | Full database (embedded mode) |
| Pricing | Free tier + pay-per-read/write | Free forever (self-hosted) |
Firebase forces you into a document model. Simple queries become complex workarounds when your data has relationships:
// Firebase: Get all orders for a user, with product details
// Requires multiple queries and client-side joins
const ordersSnap = await db.collection('orders')
.where('userId', '==', userId)
.get()
const productIds = ordersSnap.docs.map(d => d.data().productId)
// Firestore 'in' queries limited to 30 items
const chunks = chunkArray(productIds, 30)
const products = []
for (const chunk of chunks) {
const snap = await db.collection('products')
.where('id', 'in', chunk)
.get()
products.push(...snap.docs.map(d => d.data()))
}
// Manual join on the client
const result = ordersSnap.docs.map(order => ({
...order.data(),
product: products.find(p => p.id === order.data().productId)
}))
-- HeliosDB Nano: one query, server-side
SELECT o.*, p.name AS product_name, p.price
FROM orders o
JOIN products p ON o.product_id = p.id
WHERE o.user_id = $1
ORDER BY o.created_at DESC;
Firebase locks you into Google's proprietary APIs. Migrating away means rewriting your entire data layer. HeliosDB uses standard SQL and wire protocols compatible with thousands of tools:
| Aspect | Firebase | HeliosDB Nano |
|---|---|---|
| Query language | Proprietary SDK | Standard SQL |
| Data export | Firebase CLI (JSON) | pg_dump, mysqldump, CSV |
| Migration path | Rewrite data layer | Change connection string |
| Compatible tools | Firebase ecosystem only | psql, DBeaver, pgAdmin, any ORM |
| Hosting options | Google Cloud only | Any server, VPS, Raspberry Pi |
-- Complex analytics impossible in Firestore
WITH monthly_revenue AS (
SELECT
DATE_TRUNC('month', created_at) AS month,
SUM(total) AS revenue,
COUNT(*) AS order_count
FROM orders
WHERE status = 'completed'
GROUP BY DATE_TRUNC('month', created_at)
)
SELECT
month,
revenue,
order_count,
LAG(revenue) OVER (ORDER BY month) AS prev_month,
ROUND((revenue - LAG(revenue) OVER (ORDER BY month))
/ LAG(revenue) OVER (ORDER BY month) * 100, 1) AS growth_pct
FROM monthly_revenue
ORDER BY month DESC;
Firebase transactions are limited to document-level operations. HeliosDB supports multi-table ACID transactions with savepoints:
BEGIN;
SAVEPOINT before_transfer;
UPDATE accounts SET balance = balance - 500 WHERE id = 1;
UPDATE accounts SET balance = balance + 500 WHERE id = 2;
-- Verify no negative balance
SELECT balance FROM accounts WHERE id = 1;
-- If negative: ROLLBACK TO SAVEPOINT before_transfer;
INSERT INTO transfers (from_id, to_id, amount, created_at)
VALUES (1, 2, 500, NOW());
COMMIT;
-- Test schema migrations safely
CREATE BRANCH feature_v2 FROM main;
USE BRANCH feature_v2;
ALTER TABLE users ADD COLUMN tier TEXT DEFAULT 'free';
ALTER TABLE orders ADD COLUMN discount DECIMAL(5,2);
-- Run test suite against branch
-- If everything passes:
MERGE BRANCH feature_v2 INTO main;
Firebase requires Cloud Functions (external Node.js runtime with cold starts). HeliosDB runs logic inside the database:
-- Server-side logic, no cold starts, no separate deployment
CREATE FUNCTION process_order(order_id INT) RETURNS VOID AS $$
BEGIN
UPDATE orders SET status = 'processing' WHERE id = order_id;
UPDATE inventory SET quantity = quantity - (
SELECT quantity FROM order_items WHERE order_id = order_id
);
INSERT INTO audit_log (action, entity_id, timestamp)
VALUES ('order_processed', order_id, NOW());
END;
$$ LANGUAGE plpgsql;
-- Semantic search built into the database
CREATE TABLE articles (
id SERIAL PRIMARY KEY,
title TEXT,
body TEXT,
embedding VECTOR(768)
);
-- Find similar articles
SELECT title, embedding <=> $1 AS similarity
FROM articles
ORDER BY similarity
LIMIT 5;
Firebase charges per read, write, and data stored. Costs grow unpredictably with traffic:
| Usage Level | Firebase (est.) | HeliosDB Nano |
|---|---|---|
| Hobby (10K reads/day) | Free tier | Free (self-hosted) |
| Startup (1M reads/day) | $50-150/mo | Free (self-hosted) |
| Growth (10M reads/day) | $500-2,000/mo | Free (self-hosted) |
| Scale (100M reads/day) | $5,000-20,000/mo | Free (self-hosted) |
With HeliosDB Nano, your only cost is the server you run it on. A $5/month VPS can handle millions of queries per day.
Firebase might be a better fit if you need:
# Export Firestore to JSON
firebase firestore:export gs://your-bucket/export
# Convert to SQL (using heliosdb-migrate tool)
heliosdb-migrate --from firestore --input ./export --output schema.sql
-- Firestore collection: users/{userId}
-- becomes a SQL table with proper types and constraints
CREATE TABLE users (
id SERIAL PRIMARY KEY,
firebase_uid TEXT UNIQUE,
email TEXT NOT NULL,
display_name TEXT,
photo_url TEXT,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Nested sub-collections become related tables with foreign keys
CREATE TABLE user_settings (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id) ON DELETE CASCADE,
theme TEXT DEFAULT 'light',
notifications BOOLEAN DEFAULT true,
preferences JSONB DEFAULT '{}'
);
// Before: Firebase
import { getFirestore, collection, query, where, getDocs } from 'firebase/firestore'
const db = getFirestore()
const q = query(collection(db, 'users'), where('active', '==', true))
const snap = await getDocs(q)
const users = snap.docs.map(d => ({ id: d.id, ...d.data() }))
// After: HeliosDB Nano (REST API)
import { createClient } from '@heliosdb/client'
const db = createClient('http://localhost:8080', 'your-anon-key')
const { data: users } = await db.from('users').select('*').eq('active', true)
Get started with HeliosDB in minutes. Open source, free to use.