Skip to content
Back to skills

2749 Caching Strategies 3decca32

ASecurity

Next.js offre multiple strategie di caching: - **Request Memoization**: Deduplica fetch nello stesso render - **Data Cache**: Cache persistente tra request - **Full Route Cache**: Cache delle pagine statiche - **Router Cache**: Cache client-side delle route

  • 9 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 31, 2026
developmenttypescriptgonextjsnodenodejsapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned May 31, 2026

npx -y skills add tools-only/X-Skills --skill 2749-caching-strategies_3decca32 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2749 Caching Strategies 3decca32?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2749 Caching Strategies 3decca32
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2749-caching-strategies-3decca32/badge)](https://www.skillsdirectory.com/skills/tools-only-2749-caching-strategies-3decca32)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# Caching Strategies in Next.js

## Overview

Next.js offre multiple strategie di caching:
- **Request Memoization**: Deduplica fetch nello stesso render
- **Data Cache**: Cache persistente tra request
- **Full Route Cache**: Cache delle pagine statiche
- **Router Cache**: Cache client-side delle route

---

## Fetch Caching

### Default Behavior (Next.js 15+)

```typescript
// BEFORE (Next.js 14) - Cache di default
fetch('https://api.example.com/data') // cached

// AFTER (Next.js 15) - No cache di default
fetch('https://api.example.com/data') // no-store
fetch('https://api.example.com/data', { cache: 'force-cache' }) // cached
```

### Cache Time-based (ISR)

```typescript
// Revalidazione automatica ogni 60 secondi
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    next: {
      revalidate: 60, // secondi
      tags: ['products'],
    },
  })
  return res.json()
}
```

### On-demand Revalidation

```typescript
// app/api/revalidate/route.ts
import { revalidateTag, revalidatePath } from 'next/cache'
import { NextRequest } from 'next/server'

export async function POST(request: NextRequest) {
  const { tag, path } = await request.json()

  try {
    if (tag) {
      revalidateTag(tag)
      return Response.json({ revalidated: true, tag })
    }

    if (path) {
      revalidatePath(path)
      return Response.json({ revalidated: true, path })
    }

    return Response.json({ error: 'Missing tag or path' }, { status: 400 })
  } catch (error) {
    return Response.json({ error: 'Revalidation failed' }, { status: 500 })
  }
}

// Uso da webhook o admin
await fetch('/api/revalidate', {
  method: 'POST',
  body: JSON.stringify({ tag: 'products' }),
})
```

---

## unstable_cache

### Cache di Funzioni

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

// BEFORE - Query ad ogni richiesta
async function getProducts() {
  return db.product.findMany({ include: { category: true } })
}

// AFTER - Cache con revalidation
const getCachedProducts = unstable_cache(
  async () => {
    return db.product.findMany({ include: { category: true } })
  },
  ['products'], // Cache key
  {
    revalidate: 3600, // 1 ora
    tags: ['products', 'inventory'],
  }
)

// Uso nel componente
export default async function ProductPage() {
  const products = await getCachedProducts()
  return <ProductList products={products} />
}
```

### Cache con Parametri

```typescript
const getCachedProduct = unstable_cache(
  async (id: string) => {
    return db.product.findUnique({ where: { id } })
  },
  ['product'], // Key base
  { tags: ['products'] }
)

// Cache key finale: ['product', '123']
const product = await getCachedProduct('123')
```

---

## Route Segment Config

### Static vs Dynamic

```typescript
// app/page.tsx

// Static (default se no dynamic data)
export const dynamic = 'auto'

// Forza statico
export const dynamic = 'force-static'

// Forza dinamico (no cache)
export const dynamic = 'force-dynamic'

// Error se usa dynamic data
export const dynamic = 'error'

// Revalidation
export const revalidate = 3600 // 1 ora
export const revalidate = false // Mai (default static)
export const revalidate = 0 // Ogni richiesta (dynamic)
```

### Runtime

```typescript
// Edge runtime (più veloce, meno features)
export const runtime = 'edge'

// Node.js runtime (default, più compatibile)
export const runtime = 'nodejs'
```

---

## Server Actions Cache

```typescript
// app/actions.ts
'use server'

import { revalidatePath, revalidateTag } from 'next/cache'

export async function createProduct(formData: FormData) {
  const data = Object.fromEntries(formData)

  await db.product.create({ data })

  // Revalidate specifiche route
  revalidatePath('/products')
  revalidatePath('/admin/products')
  revalidateTag('products')

  return { success: true }
}

export async function updateProduct(id: string, data: FormData) {
  await db.product.update({ where: { id }, data: Object.fromEntries(data) })

  // Revalidate specifico
  revalidatePath(`/products/${id}`)
  revalidateTag(`product-${id}`)
  revalidateTag('products')
}
```

---

## Route Handlers Cache

```typescript
// app/api/products/route.ts

// Static route con revalidation
export const dynamic = 'force-static'
export const revalidate = 60

export async function GET() {
  const products = await db.product.findMany()
  return Response.json(products)
}
```

### Handler Dinamici con Cache

```typescript
// app/api/products/[id]/route.ts

export const dynamic = 'force-static'
export const revalidate = 3600

export async function GET(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params

  const product = await db.product.findUnique({
    where: { id },
  })

  if (!product) {
    return Response.json({ error: 'Not found' }, { status: 404 })
  }

  return Response.json(product)
}

// Genera static params per build
export async function generateStaticParams() {
  const products = await db.product.findMany({ select: { id: true } })
  return products.map((p) => ({ id: p.id }))
}
```

---

## Pattern Avanzati

### Stale-While-Revalidate Pattern

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

const getData = unstable_cache(
  async () => fetchExpensiveData(),
  ['expensive-data'],
  {
    revalidate: 3600, // 1 ora
  }
)

// Con fetch diretto
async function getFreshData() {
  const res = await fetch('https://api.example.com/data', {
    next: {
      revalidate: 3600,
    },
  })
  return res.json()
}
```

### Cache Tagging Granulare

```typescript
// Cache per differenti entità
const getUser = unstable_cache(
  async (id: string) => db.user.findById(id),
  ['user'],
  { tags: (id) => [`user-${id}`, 'users'] }
)

const getUserOrders = unstable_cache(
  async (userId: string) => db.order.findByUser(userId),
  ['user-orders'],
  { tags: (userId) => [`user-${userId}-orders`, 'orders'] }
)

// Invalidazione selettiva
revalidateTag('user-123')      // Solo user
revalidateTag('user-123-orders') // Solo orders di user-123
revalidateTag('orders')         // Tutti gli orders
revalidateTag('users')          // Tutti gli users
```

### Cache con Headers Condizionali

```typescript
// Route handler con ETag
export async function GET() {
  const data = await getData()
  const etag = generateETag(data)

  const headers = new Headers()
  headers.set('ETag', etag)
  headers.set('Cache-Control', 'public, max-age=3600, stale-while-revalidate=86400')

  return new Response(JSON.stringify(data), { headers })
}
```

---

## Best Practices

```typescript
// ✅ SÌ: Cache con tags significativi
const getData = unstable_cache(fetchData, ['key'], {
  revalidate: 3600,
  tags: ['entity-type', 'entity-id'],
})

// ✅ SÌ: Revalidate selettivo
revalidateTag('user-123') // Non tutto 'users'

// ✅ SÌ: Differenti TTL per differenti dati
// Dati raramente modificati: lungo TTL
const getCategories = unstable_cache(fetchCategories, ['categories'], {
  revalidate: 86400, // 24 ore
})

// Dati frequentemente modificati: breve TTL
const getComments = unstableCache(fetchComments, ['comments'], {
  revalidate: 60, // 1 minuto
})

// ❌ NON: Cache di tutto con stesso TTL
// ❌ NON: Dimenticare di revalidate dopo mutation
// ❌ NON: Usare cache per dati utente-specifici senza key appropriata
```

Files in this skill

  • README.md1.2 KB
  • skill.md7.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.