typescriptadvanced

Data Caching with unstable_cache

Cache database queries and expensive computations with unstable_cache for server-side memoization.

typescript
import { unstable_cache } from 'next/cache';

// Cache a database query
const getUser = unstable_cache(
  async (userId: string) => {
    // This runs once and is cached
    const user = await db.user.findUnique({
      where: { id: userId },
      include: { profile: true },
    });
    return user;
  },
  ['user'],       // Cache key prefix
  {
    tags: ['users'],  // For revalidation
    revalidate: 300,  // 5 minutes
  }
);

// Cache with dynamic key parts
const getPostsByCategory = unstable_cache(
  async (category: string, page: number) => {
    return db.post.findMany({
      where: { category, published: true },
      skip: (page - 1) * 10,
      take: 10,
      orderBy: { createdAt: 'desc' },
    });
  },
  ['posts-by-category'],
  {
    tags: ['posts'],
    revalidate: 60,
  }
);

// Cache expensive computation
const getAnalytics = unstable_cache(
  async (startDate: string, endDate: string) => {
    const [visitors, pageViews, revenue] = await Promise.all([
      db.$queryRaw`SELECT COUNT(DISTINCT user_id) FROM events WHERE date BETWEEN ${startDate} AND ${endDate}`,
      db.$queryRaw`SELECT COUNT(*) FROM page_views WHERE date BETWEEN ${startDate} AND ${endDate}`,
      db.$queryRaw`SELECT SUM(amount) FROM transactions WHERE date BETWEEN ${startDate} AND ${endDate}`,
    ]);
    return { visitors, pageViews, revenue };
  },
  ['analytics'],
  {
    tags: ['analytics'],
    revalidate: 3600,
  }
);

// Usage in Server Component
export default async function Dashboard() {
  const user = await getUser('user-123');
  const posts = await getPostsByCategory('tech', 1);
  return <div>{user?.name}</div>;
}

Use Cases

  • database query caching
  • expensive computations
  • reducing DB load

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.