Course contentsShow
← Node.js
Lesson 808 of 1,9808. Data Persistence and ORM IntegrationFree lesson

Caching Query Results in ORMs

You will implement model-level caching to reduce redundant database calls.

In this lesson, you will learn how to implement model-level caching to prevent redundant database queries and speed up your Node.js application.

Why Cache Query Results?

Even with efficient ORM usage like eager loading, querying the database for the same record repeatedly is expensive. Every database round-trip incurs latency. By storing the results of frequent queries in your application’s memory (or a dedicated store like Redis), you can serve subsequent requests instantly without hitting your database server.

Implementing Model-Level Caching

The most common way to implement this is by using Lifecycle Hooks. When a record is updated or deleted, the cached version becomes "stale" (outdated). We must use hooks to invalidate or clear that cache whenever the data changes.

Here is a simplified example of how you might wrap a find operation:

const cache = new Map(); // Simple in-memory cache

async function getUserById(id) {
  const cacheKey = `user_${id}`;
  
  // 1. Check if the result exists in cache
  if (cache.has(cacheKey)) {
    return cache.get(cacheKey);
  }

  // 2. If not, fetch from the database
  const user = await User.findByPk(id);
  
  // 3. Store result for future requests
  cache.set(cacheKey, user);
  return user;
}

// 4. Use an 'afterUpdate' lifecycle hook to clear stale data
User.afterUpdate((user) => {
  cache.delete(`user_${user.id}`);
});

The Strategy

When implementing this, keep three things in mind:

  1. Cache Keying: Always create unique keys based on the query parameters so different records don't overwrite each other.
  2. Invalidation: The most difficult part of caching is knowing when to clear it. Use afterUpdate, afterDestroy, and afterCreate hooks to ensure your cache doesn't serve old, incorrect data.
  3. Memory Limits: If using an in-memory store (like a Map), be careful not to store too many items, as you could run out of server RAM. For production apps, external stores like Redis are preferred.

Key Takeaway: Caching query results reduces database load, but you must use lifecycle hooks to invalidate cached entries whenever the underlying data is mutated to ensure data consistency.