Count Cachula

Local-first performance without the complexity πŸ§›

Stale-while-revalidate on steroids: instant UI updates with cache tags, invalidation, and smart preloading

Get started:

Terminal window
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.