Пошук уроків, статей та іншого контенту
Організуєте групове оновлення пов’язаних даних через теги та функцію revalidateTag.
revalidateTagrevalidateTag дає змогу об’єднати кілька кешованих запитів спільним тегом, а потім одним викликом позначити всі ці дані як застарілі.
Це зручно, коли одна дія змінює кілька пов’язаних наборів даних:
список товарів;
категорії товарів;
рекомендовані товари;
статистику або інші похідні дані.
Замість того щоб окремо оновлювати кожен URL або маршрут, усім запитам призначають один тег:
catalogПісля цього викликають:
revalidateTag('catalog', 'max')Next.js позначить усі кешовані записи з тегом catalog як застарілі.
fetchТег задають у властивості next.tags:
const response = await fetch('https://api.example.com/products', {
cache: 'force-cache',
next: {
tags: ['catalog'],
},
})Тепер результат цього запиту пов’язаний із тегом catalog.
Один запит може мати кілька тегів:
const response = await fetch('https://api.example.com/products/42', {
cache: 'force-cache',
next: {
tags: ['catalog', 'product:42'],
},
})Такий запис можна оновити як разом з усім каталогом через тег catalog, так і окремо через тег product:42.
const [productsResponse, categoriesResponse] = await Promise.all([
fetch('https://api.example.com/products', {
cache: 'force-cache',
next: {
tags: ['catalog'],
},
}),
fetch('https://api.example.com/categories', {
cache: 'force-cache',
next: {
tags: ['catalog'],
},
}),
])Обидва результати мають тег catalog. Один виклик revalidateTag оновить обидва записи.
revalidateTagФункція імпортується з next/cache:
import { revalidateTag } from 'next/cache'Її потрібно викликати на сервері:
у Server Action;
у Route Handler;
в іншому серверному коді, де доступні серверні API Next.js.
Рекомендований варіант:
revalidateTag('catalog', 'max')Другий аргумент 'max' визначає stale-while-revalidate-поведінку:
поточний запит може отримати застарілі дані;
Next.js позначає запис як неактуальний;
наступний процес оновлює дані у фоні;
подальші запити отримують свіжий результат.
revalidateTag не виконує новий HTTP-запит одразу в момент виклику. Функція лише змінює стан кешу.
Нехай сторінка каталогу отримує товари й категорії з API. Обидва запити мають тег catalog. Після зміни даних адміністратор натискає кнопку оновлення.
// app/catalog/actions.ts
'use server'
import { revalidateTag } from 'next/cache'
export async function refreshCatalog() {
// Усі кешовані запити з цим тегом стануть неактуальними
revalidateTag('catalog', 'max')
}// app/catalog/page.tsx
import { refreshCatalog } from './actions'
type Product = {
id: number
name: string
price: number
}
type Category = {
id: number
name: string
}
async function getProducts(): Promise<Product[]> {
const response = await fetch(
`${process.env.CATALOG_API_URL}/products`,
{
cache: 'force-cache',
next: {
tags: ['catalog'],
},
},
)
if (!response.ok) {
throw new Error('Не вдалося завантажити товари')
}
return response.json()
}
async function getCategories(): Promise<Category[]> {
const response = await fetch(
`${process.env.CATALOG_API_URL}/categories`,
{
cache: 'force-cache',
next: {
tags: ['catalog'],
},
},
)
if (!response.ok) {
throw new Error('Не вдалося завантажити категорії')
}
return response.json()
}
export default async function CatalogPage() {
const [products, categories] = await Promise.all([
getProducts(),
getCategories(),
])
return (
<main>
<h1>Каталог</h1>
<p>Категорій: {categories.length}</p>
<ul>
{products.map((product) => (
<li key={product.id}>
{product.name} — {product.price} грн
</li>
))}
</ul>
<form action={refreshCatalog}>
<button type="submit">Оновити каталог</button>
</form>
</main>
)
}Після виконання refreshCatalog обидва запити з тегом catalog будуть визнані неактуальними. Коли сторінка або інший серверний компонент звернеться до цих даних знову, Next.js отримає свіжі значення.
Для прикладу потрібно визначити адресу API:
CATALOG_API_URL=https://api.example.comОновлення можна запускати не лише через Server Action, а й через HTTP-ендпойнт:
// app/api/catalog/revalidate/route.ts
import { revalidateTag } from 'next/cache'
export async function POST() {
revalidateTag('catalog', 'max')
return Response.json({
revalidated: true,
tag: 'catalog',
})
}Після цього клієнт або зовнішня система може надіслати POST-запит до:
/api/catalog/revalidateУ реальному застосунку такий ендпойнт потрібно захистити автентифікацією або секретним токеном. Інакше будь-хто зможе запускати оновлення кешу.
Для групового оновлення використовують спільний тег:
next: {
tags: ['catalog'],
}Для точкового оновлення можна додати тег із ідентифікатором:
async function getProduct(id: string) {
const response = await fetch(
`https://api.example.com/products/${id}`,
{
cache: 'force-cache',
next: {
tags: ['catalog', `product:${id}`],
},
},
)
if (!response.ok) {
throw new Error('Не вдалося завантажити товар')
}
return response.json()
}Тепер доступні два рівні оновлення:
// Оновити всі дані каталогу
revalidateTag('catalog', 'max')
// Оновити лише товар із конкретним ідентифікатором
revalidateTag('product:42', 'max')Це дає змогу не скидати весь каталог, коли змінився лише один товар.
revalidateTag та інші джерела данихТеги можуть використовуватися не лише з fetch. Кешованим даним, отриманим через unstable_cache, також можна призначати теги:
import { unstable_cache } from 'next/cache'
const getProducts = unstable_cache(
async () => {
// Тут може бути запит до бази даних
return database.product.findMany()
},
['products'],
{
tags: ['catalog'],
},
)Після цього виклик:
revalidateTag('catalog', 'max')позначить цей кешований результат як неактуальний.
Важливо: тег не змінює дані у зовнішній базі даних або API. Він лише повідомляє Next.js, що кешований результат потрібно оновити під час наступного використання.
Теги підходять, коли:
кілька запитів належать до однієї функціональної області;
одна мутація впливає на кілька кешованих запитів;
потрібно оновлювати дані незалежно від конкретного URL;
один ресурс відображається на кількох сторінках;
потрібно поєднати групове та точкове оновлення.
Наприклад, після зміни товару можна викликати:
revalidateTag('product:42', 'max')
revalidateTag('catalog', 'max')Перший тег оновлює сторінки конкретного товару, а другий — списки й інші представлення каталогу.
revalidateTag у клієнтському компонентіЦе серверна функція. Її не можна викликати безпосередньо з компонента з директивою:
'use client'Клієнтський код має викликати Server Action або Route Handler, а вже той — revalidateTag.
Цей запит не буде пов’язаний із тегом:
await fetch('https://api.example.com/products')Виклик:
revalidateTag('catalog', 'max')не вплине на нього, якщо тег catalog не було вказано в параметрах кешування.
revalidateTag не завантажує дані одразу:
revalidateTag('catalog', 'max')Функція лише позначає кеш як неактуальний. Нове завантаження відбудеться, коли ці дані знадобляться наступному запиту.
Теги є звичайними рядками. Помилка в одному символі створює інший тег:
next: {
tags: ['catalog'],
}і:
revalidateTag('catlog', 'max')не пов’язані між собою.
Для тегів краще використовувати спільні константи:
// app/lib/cache-tags.ts
export const CACHE_TAGS = {
catalog: 'catalog',
product: (id: string) => `product:${id}`,
} as constТоді використання виглядає узгоджено:
import { revalidateTag } from 'next/cache'
import { CACHE_TAGS } from '@/app/lib/cache-tags'
revalidateTag(CACHE_TAGS.catalog, 'max')Тег має сенс лише для даних, які потрапляють у кеш. Якщо запит щоразу виконується без кешування, інвалідація його запису не дасть практичного результату.
revalidateTag інвалідовує кешовані дані за спільним тегом.
Теги додають до fetch через next.tags.
Один тег може бути призначений багатьом незалежним запитам.
revalidateTag('catalog', 'max') позначає всі записи з тегом catalog як застарілі.
Функцію потрібно викликати на сервері — у Server Action або Route Handler.
Теги не змінюють дані у базі даних чи зовнішньому API.
Для точкового оновлення зручно поєднувати групові теги на кшталт catalog з тегами окремих записів на кшталт product:42.