← notes

When Redis Stops Being Enough

2026redissupabasedatabases

I built StrikeIt with Upstash Redis as the data store. The choice made sense at the time - free tier, zero setup, HTTP-based so I could hit it from anywhere. I stored everything under one key: a JSON blob with all the lists and tasks serialized inside. It worked fine for a week.

Then it didn't.

The first sign was bandwidth. Every API call - even one that only needed a single task - loaded the entire blob. The more data accumulated, the worse it got. Writes were replace-the-whole-blob operations. Race conditions were one concurrent request away.

I stored data like this:

SET user:123:data '{"lists": [{"id": "abc", "tasks": [...hundreds of tasks...]}]}'

That's it. One key. Every request deserializes the whole thing, every write re-serializes it.

The actual problem: I was treating Redis like a database when it's a cache. It has no real query primitives, no indexes on arbitrary fields, no joins. It's fast at what it does - key lookups, expiring tokens, atomic increments. For relational data with query patterns, it's the wrong tool.

Switching to Supabase was straightforward. Per-row storage meant I could query exactly what I needed. The schema ended up being:

create table items (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users,
  content text not null,
  done boolean default false,
  created_at timestamptz default now()
);

Supabase realtime came for free. Instead of polling every 2 seconds, the client subscribes to row changes. One tab marks something done, every other tab sees it immediately. That replaced about 30 lines of polling code.

The honest tradeoffs: Supabase on the free tier has cold starts. The first request after inactivity is noticeably slow - Redis doesn't have that. And Redis is genuinely good for rate limiting; I still use it for that in other projects. The issue was using it as the source of truth for user data.

What I'd do differently: ask earlier what shape the data is. If the answer is "rows with foreign keys," use a database. If the answer is "ephemeral counters," Redis is the right call. I just didn't ask the question.