Local-first performance without the complexity π§
Stale-while-revalidate on steroids: instant UI updates with cache tags, invalidation, and smart preloading
Get started:
npm install @countcachula/core
β‘ Fast
Serve cached content instantly while revalidating in the background
π Fresh
Automatic cache invalidation keeps your data up-to-date
π― Simple
Drop-in replacement for fetch with powerful caching
What is Count Cachula?
An alternative to local-first that gives you the same instant, responsive UXβbut simpler, lighter, and easier to reason about.
The Local-First Promise
Local-first architectures provide instant UI updates by keeping a local copy of your data. But they come with serious complexity: CRDTs, conflict resolution, sync protocols, offline queues, and complex state management.
The problem: You're building distributed systems whether you want to or not.
The Count Cachula Way
Get the same instant responsiveness by treating your cache as truth. Stale-while-revalidate means users see data immediately, then get updates automatically. No sync, no conflicts, no distributed systems.
The solution: Your server is still the source of truth. The cache just makes it feel instant.
Stale-While-Revalidate on Steroids
β‘
Instant Response
Serve from cache immediately while fetching fresh data in the background
π·οΈ
Cache Tags
Tag related data and invalidate entire groups at once
π
Smart Invalidation
Automatically invalidate caches when mutations happen via SSE
π
Preloading
Warm up caches before users need them for zero-latency navigation
Why This Works
β
Users see data instantly
Cached data appears immediately, no loading spinners
β
Data stays fresh automatically
Background revalidation and SSE invalidation keep everything current
β
Server remains source of truth
No conflict resolution, no CRDTs, no distributed systems complexity
β
Drop-in replacement for fetch
Works with your existing API, no architecture overhaul needed
How It Works
Count Cachula uses server-driven invalidation and preloading to keep your client perfectly in syncβwithout any client-side complexity.
1
Browser Connects to SSE
When your app loads, it establishes a Server-Sent Events connection. This gives the server a direct channel to push updates to the client.
2
Server Sends Preload Hints
The server can immediately start warming up the cache by sending preload hints for important APIs:
event: preload-hint
data: {"url": "/api/tasks", "tags": ["tasks"]}
event: preload-hint
data: {"url": "/api/user/profile", "tags": ["user"]}
The client fetches and caches this data in the background, so it's ready instantly when needed.
3
Code Requests Data
When your code needs data, it makes a normal request using Count Cachula's fetch:
// When your code needs data
const request = new Request('/api/tasks');
const tasks = await CountCachula.fetch(request);
// Returns cached data instantly (if available)
// Then fetches fresh data in background
// UI updates automatically when fresh data arrives
The magic: Returns cached data instantly, then fetches fresh data in the background and automatically updates your UI when it arrives. No loading states needed!
4
Mutations Invalidate Tags
When you mutate data on the server, you invalidate related cache tags:
// After a mutation on the server
await db.tasks.create(newTask);
// Invalidate related cache tags
hub.invalidate(['tasks', 'user:stats']);
// SSE automatically notifies all connected clients
The server pushes invalidation events via SSE to all connected clients:
event: invalidate
data: {"tags": ["tasks", "task:123"]}
5
Client Updates Automatically
When the client receives invalidation events, it automatically refetches any affected Observables and updates your UI.
π― This gives you REAL-TIME updates!
All users see changes instantly, even on pages they're not currently viewing. The cache stays warm and ready.
π Server-Driven Everything
The server controls invalidation and preloading, which means the client is never stale. Every fetch gets fresh data in the background, and SSE pushes immediate updates when things change.
π Progressive Enhancement
Start simple with just CountCachula.fetch() for automatic stale-while-revalidate. Add SSE for real-time updates. Add cache tags for smart invalidation. Add preloading for instant navigation. Build up complexity only when you need it.
π Documentation
Comprehensive guides for each package
Ready to see Count Cachula in action?
π View Live Demo