I Added Redis Caching to My E-Commerce Backend, and the Performance Difference Surprised Me
High-read endpoints were re-running the same database joins on every request, pushing response times to 150-250ms. Here's how a cache-aside Redis layer cut that to single-digit milliseconds.

A few of the highest-traffic endpoints on an e-commerce backend I was working on had one thing in common: they ran the same database joins on every single request. Product listings, category pages, cart lookups — none of that data changes on every request, but the database didn't know that. Response times were sitting around 150–250ms, and load testing made it obvious the database was doing far more work than it needed to.
The Problem
None of these endpoints were slow because of bad queries. They were slow because they were re-deriving the same answer over and over. Products and categories change occasionally; carts change per user, but not on every read. Hitting Postgres for all of it on every request was the actual bottleneck, not any single query plan.
Where Redis Went In
I added a Redis layer in front of the reads that mattered most:
- Product lists and categories — the highest-read, lowest-write data in the system
- Cart sessions — read far more often than they're modified
- Rate-limiting counters — a natural fit for Redis's in-memory counters
The pattern was cache-aside: check Redis first, fall back to the database on a miss, then populate the cache for next time. Each entry got a TTL so stale data couldn't live forever, and writes (create/update operations) explicitly invalidated the relevant cache keys instead of waiting for the TTL to expire.
The Results
The difference wasn't subtle. Endpoints that were taking 150–250ms dropped to single-digit milliseconds — around 2–5ms on a cache hit. Database load dropped just as sharply once the repeated joins stopped hitting Postgres on every request. Watching it happen with redis-cli MONITOR and checking memory behavior with INFO memory made it clear the cache was actually absorbing the traffic it was supposed to.
What I Learned
Adding Redis wasn't just a speed optimization — it forced a real separation between reads and writes. Once data has a cache layer in front of it, every write path needs to know what it just invalidated, and every read path needs to know it might be looking at slightly stale data. That's an architectural decision, not a performance tweak.
It also opened up questions I hadn't needed to think about before: what eviction policy makes sense as the cache grows, how to warm the cache after a deploy instead of taking a cold-cache traffic spike, and where distributed locks become necessary — checkout flows being the obvious one, where two requests touching the same inventory can't both assume they're working with fresh data.
Found this useful?
Share it with the dev community or cross-post with a canonical link back here.
Cross-posting to dev.to or Hashnode? Use this as your canonical URL: https://www.codenovix.com/blog/redis-caching-ecommerce-backend-performance
Related articles

Using Exclude and Expose in NestJS Entities to Control API Responses
A hashed password still shouldn't leave your API. class-transformer's @Exclude and @Expose decorators make an entity define what's safe to serialize, instead of trusting every controller to filter it manually.

How Designing a Clean Backend Folder Structure Improved Performance and Scalability
A messy backend structure doesn't just look bad — it slows down debugging, onboarding, and scaling. Here's how reorganizing folders into Core, Config, Module, and Shared layers changed both.

Cypress Testing: Balancing Velocity and Stability in Modern Engineering
Cypress runs inside the browser instead of driving it remotely, which is why its automatic waiting kills flaky tests instead of just hiding them. Speed and stability aren't a tradeoff — they're the same investment.