#010Everwood BookingExperimentingIntermediate

Why I Added Redis to My Booking Dashboard and Only Let It Do One Thing

The appointment page is the most-visited page in the Everwood dashboard. Business owners reload it all day. I added Redis as a read cache for appointment records, but I intentionally kept it out of everything else. The database stays the source of truth. Redis just makes reads faster.

By the Everwood founder · June 23, 2026 · 7 min read

Tools
RedisSupabaseNext.jsTypeScript
Concepts
Read CacheCache InvalidationMulti-Tenant SafetyScoped PerformanceWrite-Through Invalidation
Mission Brief
Goal

Make the appointment page faster for business owners who check it repeatedly throughout the day, without introducing caching complexity into the rest of the booking system.

Problem

The appointment page hits Supabase/Postgres on every load. For a page that gets refreshed many times a day by the same business, that means repeated identical queries for data that has not changed.

Lesson

Performance work should be scoped. Redis has one job in Everwood: make appointment list reads faster. That constraint is what makes the architecture safe.

Read as

The Build

The appointment page is one of the most important pages in the Everwood booking dashboard. For a business owner, this is not a page they open once and forget about. This is the page they may keep open all day. They check who is coming in, what time each appointment starts, which worker is assigned, and what the day looks like.

So I started thinking about performance differently. It is not enough for the page to work. It needs to feel fast.

The booking product uses Supabase/Postgres as the source of truth. That is the right choice. Appointment records need to be accurate. When someone creates, updates, cancels, or deletes an appointment, the database should be the place where that truth lives.

But the appointment page is read-heavy. A business might refresh it many times throughout the day. They switch date ranges, check today's schedule, come back after helping a customer, or leave it open while working. The same appointment records get requested over and over again.

That made me ask: can I make appointment reads faster without making the booking system less reliable?

The Problem

Every time the appointment page loads, it queries Supabase/Postgres for the business's appointment records. That query is correct. It returns the right data. But it runs the same query every time, even when nothing has changed since the last load.

For a salon owner checking their schedule between clients, the page needs to respond quickly. They are not sitting at a desk waiting for a dashboard to load. They are between customers, checking their phone or glancing at a tablet. If the page feels slow or heavy, it adds friction to the thing they are already doing all day.

The question was not whether the page was broken. It worked. The question was whether it could be faster for the most common case: a business owner viewing appointment data that has not changed since the last time they looked.

Assumption vs Reality

What I Thought Would Work

Once you add Redis to a project, you should use it broadly. Cache business settings, worker data, service data, availability, product access. More caching means more speed.

What Actually Happened

More caching means more places where stale data can cause bugs. In a booking system, availability checks and conflict prevention have to hit the database. Caching those would trade correctness for speed. The right move was to use Redis for exactly one thing and keep everything else on the database.

The Fix

I added Redis as a read cache for appointment records only.

The appointment page now follows this pattern. The business user opens the appointment page. The app checks Redis for appointment records for that business and date range. If Redis has the data, the page loads from cache. If Redis does not have the data, the app queries Supabase/Postgres, stores the result in Redis with a short TTL, and returns the appointment list.

The cached entry is filed under the business it belongs to and the exact date range it covers. That means one business cannot accidentally receive another business's appointment data from the cache. Multi-tenant safety is more important than speed.

For cache invalidation, when an appointment is created, updated, canceled, or deleted, the app clears the appointment cache for that business. The next time the appointment page loads, it fetches fresh data from Supabase/Postgres and stores the updated list in Redis again.

The cache also uses a short TTL of around 60 seconds. That means even if invalidation misses something, the cached data does not live forever.

The rule I set for myself was simple: Redis can help read appointments faster, but it cannot decide whether an appointment is valid. All writes, all availability checks, all conflict prevention still go through the database.

Lesson Unlocked

Performance work should be scoped. It is tempting to add Redis everywhere once you add it to the project. But that can make the system harder to debug and harder to trust. A booking system has to be correct. A cached availability check that returns stale data could cause a double booking. A cached business setting that lags behind an update could show the wrong hours. Instead of caching broadly, I gave Redis one clear job: the appointment page is frequently visited and read-heavy, so appointment reads should be cached. The database is still responsible for truth. Redis is responsible for speed. The biggest win is that this improves performance without changing the ownership of the data. Appointments are sensitive operational data. They need to be correct. They affect real customers and real businesses. So I did not want Redis to become a second database for appointments. I wanted it to be a helper. A fast layer. A cache. Nothing more.

Business Translation

From the business owner's perspective, this change is not about Redis. They do not care what cache I use. They care that the appointment page feels responsive when they are running their business. If they are checking today's schedule multiple times a day, the page should not feel slow or heavy. A faster appointment page means less friction during normal business operations. That is the real goal.

Builder Notes

Redis is scoped to one use case. The constraint is intentional and keeps the booking system safe.

Technical Note 1

Every cached entry is filed under the business it belongs to and the exact date range it covers, with a version marker built into the key so the whole cache can be retired cleanly if the shape of the data ever changes.

Technical Note 2

TTL is around 60 seconds. Short enough that stale data self-corrects even if explicit invalidation misses.

Technical Note 3

Cache invalidation fires on appointment create, update, cancel, and delete. It clears the appointment cache for the affected business, not all businesses.

Technical Note 4

Redis is not used for: availability calculations, double-booking prevention, appointment writes, business settings, worker data, service data, product subscriptions, feature access, audit logs, chat logs, or analytics.

Technical Note 5

Isolation between businesses is enforced by the filing system itself. Each business has its own entries, and there is no shared appointment cache that one business could read another out of.

Technical Note 6

The next step is measuring: how long does the appointment query take without Redis, how often the same range is requested, how much faster cache hits feel, and whether invalidation behaves correctly after changes.

Before / After

Before
  • Every appointment page load queries Supabase/Postgres directly
  • Repeated views of the same date range run the same query each time
  • No caching layer for any dashboard data
  • Page speed depends entirely on database response time
After
  • Appointment reads check Redis first, fall back to Supabase/Postgres on miss
  • Cache key is scoped to business + date range for multi-tenant safety
  • Cache invalidation fires on every appointment write
  • Short TTL ensures stale data self-corrects even if invalidation is missed
  • All writes, availability checks, and conflict prevention still hit the database

What I'd Do Differently

I would define the caching boundary before writing any Redis code. The first instinct is to start caching and figure out the scope later. But in a booking system, caching the wrong thing can cause real problems. Starting with a written rule like "Redis can help read appointments faster, but it cannot decide whether an appointment is valid" would have saved me from even considering broader caching before the narrow case was proven.

Next Experiment

Measuring the appointment page before and after Redis caching. I want to see how long the appointment query takes without Redis, how often the same appointment range is requested, how much faster the page feels with cache hits, and whether cache invalidation behaves correctly after appointment changes. Once that is working well, I can decide whether other read-heavy pages deserve caching too.

PerformanceCachingBooking WidgetSaaS Architecture

The information on this website is provided for general educational purposes only and may not apply to your specific setup or environment. It should not be considered professional advice. Always consult a qualified technician when appropriate.