Zero is built on a three-tier architecture that enables real-time sync between PostgreSQL and client applications. Understanding this architecture is key to working effectively with Zero.
Overview
The Three Tiers
PostgreSQL: Source of Truth
PostgreSQL serves as the authoritative source of truth for all data in Zero applications.
Role : Primary database storing canonical application data
Changes : All mutations ultimately write to PostgreSQL
Replication : Uses PostgreSQL’s logical replication to stream changes
Zero doesn’t modify your PostgreSQL schema. You can use standard PostgreSQL tools, migrations (like Drizzle), and direct SQL queries alongside Zero.
zero-cache: Server-Side Replica
zero-cache is the sync server that maintains a SQLite replica of your PostgreSQL data.
Key responsibilities:
Replication : Continuously replicates changes from PostgreSQL using logical replication
Query Processing : Transforms ZQL queries into SQL and maintains incremental views
Authorization : Enforces permissions defined in your schema
Client Sync : Manages WebSocket connections to clients and pushes updates
Implementation details:
The replicator service continuously syncs data:
// From packages/zero-cache/src/services/replicator/incremental-sync.ts
export class IncrementalSyncer {
async run ( lc : LogContext ) {
const { watermark : initialWatermark } = getSubscriptionState ( this . #replica );
// Notify subscribers that replica is ready
void this . #notifier . notifySubscribers ();
while ( this . #state . shouldRun ()) {
const { replicaVersion , watermark } = getSubscriptionState ( this . #replica );
downstream = await this . #changeStreamer . subscribe ({
protocolVersion: PROTOCOL_VERSION ,
watermark ,
replicaVersion ,
});
for await ( const message of downstream ) {
const result = processor . processMessage ( lc , message );
if ( result ?. watermark && result ?. changeLogUpdated ) {
void this . #notifier . notifySubscribers ({ state: 'version-ready' });
}
}
}
}
}
Why SQLite?
Fast query execution for client views
Embeddable: runs in the same process as zero-cache
Efficient indexes for incremental view maintenance
Low-latency reads compared to PostgreSQL round-trips
Client: Replicache + IndexedDB
Clients use Replicache to maintain a local cache in IndexedDB.
Key features:
Local-First : Reads are instant from IndexedDB
Optimistic Updates : Mutations apply locally first, then sync to server
Offline Support : Works without connectivity; syncs when reconnected
Reactive Queries : UI updates automatically when data changes
Client initialization:
import { Zero } from '@rocicorp/zero' ;
import { schema } from './schema' ;
const zero = new Zero ({
server: 'https://your-zero-cache-url' ,
schema ,
userID: currentUser . id ,
});
Data Flow
Write Path (Mutations)
Client mutation
Client calls a mutator (e.g., zero.mutate.updateIssue(...)).
Optimistic update
Replicache applies the mutation to IndexedDB immediately and returns.
Server sync
Mutation is pushed to zero-cache over WebSocket.
Validation
zero-cache validates permissions and executes against PostgreSQL.
Replication
PostgreSQL replicates the change back to zero-cache’s SQLite replica.
Broadcast
zero-cache broadcasts the change to all subscribed clients.
Reconciliation
Clients receive the authoritative version and reconcile with their optimistic state.
Read Path (Queries)
Query subscription
Client subscribes to a query using useQuery() or similar.
Query transformation
zero-cache transforms the ZQL query into SQL for SQLite.
Initial results
Initial query results are pushed to the client.
Incremental updates
As data changes, zero-cache uses incremental view maintenance (IVM) to compute diffs.
Real-time sync
Only the changes are pushed to clients, not the entire result set.
Key Components
zero-client
The client library (@rocicorp/zero-client) that applications import:
// From packages/zero-client/src/client/zero.ts
export class Zero {
readonly #replicache : ReplicacheImpl ;
readonly #schema : Schema ;
readonly #queryManager : QueryManager ;
readonly #connectionManager : ConnectionManager ;
// Creates query builders for each table in schema
get query () : SchemaQuery {
return createBuilder ( this . #schema );
}
// Executes mutations with optimistic updates
async mutate () {
// Mutation logic
}
}
ZQL Engine
The query language and incremental view maintenance engine:
Query Building : Type-safe query construction with method chaining
IVM Operators : Pipeline of operators (filter, join, take, etc.) that maintain incremental views
Change Processing : Efficiently computes view updates from database changes
See Queries for details.
Change Streamer
Manages the replication stream from PostgreSQL:
// Coordinates catchup and real-time streaming
export class ChangeStreamerImpl implements ChangeStreamerService {
async subscribe ( context : SubscriberContext ) : Source < Downstream > {
// Stream changes from PostgreSQL logical replication
// Handle subscriber catchup from stored changelog
// Forward new changes in real-time
}
}
Client reads : ~1ms (IndexedDB)
No server round-trip for cached queries
Incremental updates : Only diffs are synced
Optimistic response : Immediate (< 1ms)
Server confirmation : 50-200ms typical
Broadcast to clients : 10-50ms after commit
Scalability
Clients per zero-cache instance : 10,000+
SQLite query performance : 1000s of queries/sec
PostgreSQL : Scales independently for writes
Deployment Patterns
Development
# Run zero-cache locally
npm run zero-cache-dev
# Or via Docker
docker run -p 4848:4848 rocicorp/zero-cache
Production
Single Instance
Horizontal Scaling
Deploy zero-cache as a long-running service: # docker-compose.yml
services :
zero-cache :
image : rocicorp/zero-cache
environment :
- DATABASE_URL=postgresql://...
ports :
- "4848:4848"
Multiple zero-cache instances can run in parallel, each replicating from PostgreSQL independently. Clients connect to any instance via load balancer with sticky sessions.
Each zero-cache instance maintains its own SQLite replica. Clients must maintain session affinity to a single instance.
Next Steps
Sync Model Learn how Zero keeps data in sync across tiers
Schema Definition Define your tables and relationships
Queries Build type-safe queries with ZQL
Permissions Control access to your data