Пошук уроків, статей та іншого контенту
Навчитеся зберігати результати отримання даних і керувати їхнім кешуванням у Data Cache.
Data Cache у Next.js — це серверний кеш для результатів отримання даних. Він дає змогу не виконувати той самий запит до зовнішнього API під час кожного відкриття сторінки.
Кешування корисне, коли:
дані змінюються нечасто;
запит до API займає багато часу;
потрібно зменшити кількість запитів до зовнішнього сервісу;
сторінка має швидко віддавати вже отримані дані.
Data Cache працює на сервері. Дані з нього не потрапляють у браузер автоматично як окремий клієнтський кеш.
fetchУ Server Components можна використовувати звичайний fetch. Щоб явно зберегти результат у Data Cache, передайте опцію cache: 'force-cache'.
// app/page.tsx
type Post = {
id: number;
title: string;
body: string;
};
async function getPosts(): Promise<Post[]> {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
cache: 'force-cache',
});
if (!response.ok) {
throw new Error('Не вдалося отримати публікації');
}
return response.json();
}
export default async function HomePage() {
const posts = await getPosts();
return (
<main>
<h1>Публікації</h1>
<ul>
{posts.slice(0, 5).map((post) => (
<li key={post.id}>
<h2>{post.title}</h2>
<p>{post.body}</p>
</li>
))}
</ul>
</main>
);
}Під час першого запиту Next.js отримує дані від API та зберігає результат у Data Cache. Наступні запити можуть використати вже збережений результат.
force-cache означає: якщо для цього запиту є актуальне значення в кеші, використати його замість нового запиту до API.
Для даних, які час від часу змінюються, можна задати період повторної перевірки за допомогою next.revalidate.
Значення вказується в секундах:
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts',
{
next: {
revalidate: 60,
},
},
);У цьому прикладі:
Next.js отримує дані та зберігає їх у кеші.
Протягом 60 секунд використовує кешоване значення.
Після цього може виконати новий запит і оновити кеш.
Цей підхід називається поступовою повторною валідацією — Incremental Static Regeneration, або ISR.
Повний приклад:
// app/page.tsx
type Post = {
id: number;
title: string;
body: string;
};
async function getPosts(): Promise<Post[]> {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
next: {
revalidate: 60,
},
});
if (!response.ok) {
throw new Error('Не вдалося отримати публікації');
}
return response.json();
}
export default async function HomePage() {
const posts = await getPosts();
return (
<main>
<h1>Останні публікації</h1>
{posts.slice(0, 5).map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.body}</p>
</article>
))}
</main>
);
}revalidate: 60 не означає, що всі користувачі рівно через 60 секунд чекатимуть на завершення запиту. Після завершення періоду Next.js отримує можливість оновити дані, а користувачі можуть тимчасово отримати попередню кешовану версію.
Для даних, які завжди мають бути найсвіжішими, використовуйте cache: 'no-store'.
async function getCurrentUser() {
const response = await fetch('https://example.com/api/user', {
cache: 'no-store',
});
if (!response.ok) {
throw new Error('Не вдалося отримати користувача');
}
return response.json();
}У цьому випадку Next.js не зберігає результат у Data Cache. Запит виконується знову під час кожного отримання даних.
no-store підходить для:
персональних даних користувача;
даних, які змінюються майже миттєво;
відповідей, які залежать від cookies або заголовків поточного запиту;
інформації, яку не можна показувати з кешу іншому запиту.
| Режим | Поведінка | |---|---| | cache: 'force-cache' | Використовує Data Cache і кешує результат | | next: { revalidate: 60 } | Кешує результат і перевіряє його актуальність через 60 секунд | | cache: 'no-store' | Не кешує результат |
Зазвичай для даних, які рідко змінюються, достатньо revalidate. Він дає кешу час життя та не вимагає вручну очищати його після кожного запиту.
Тег дає змогу пов’язати запит із певною групою даних.
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
next: {
revalidate: 3600,
tags: ['posts'],
},
});Тепер цей запит має тег posts. Теги зручні, коли в застосунку є кілька запитів, пов’язаних з одним типом даних.
Наприклад:
список публікацій;
популярні публікації;
кількість публікацій.
Усі ці запити можна позначити тегом posts, а потім оновити їх разом.
Для оновлення даних після зміни використовується revalidateTag. Його потрібно викликати на сервері, наприклад у Server Action або обробнику маршруту.
'use server';
import { revalidateTag } from 'next/cache';
export async function refreshPosts() {
revalidateTag('posts', 'max');
}Після виклику Next.js позначить кешовані дані з тегом posts як такі, що потребують повторного отримання.
Точна поведінка тегів залежить від версії Next.js. У сучасних версіях для стандартного поступового оновлення використовується форма revalidateTag('posts', 'max').
Іноді після зміни даних потрібно оновити не лише Data Cache, а й конкретну сторінку. Для цього можна використати revalidatePath.
'use server';
import { revalidatePath } from 'next/cache';
export async function refreshHomePage() {
revalidatePath('/');
}revalidatePath('/') повідомляє Next.js, що сторінку за адресою / потрібно повторно перевірити.
Зазвичай:
revalidateTag використовують для оновлення певного набору даних;
revalidatePath використовують для оновлення конкретного маршруту.
Одна сторінка може отримувати різні типи даних із різними правилами:
async function getNews() {
const response = await fetch('https://example.com/api/news', {
next: {
revalidate: 300,
},
});
if (!response.ok) {
throw new Error('Не вдалося отримати новини');
}
return response.json();
}
async function getPrivateProfile() {
const response = await fetch('https://example.com/api/profile', {
cache: 'no-store',
});
if (!response.ok) {
throw new Error('Не вдалося отримати профіль');
}
return response.json();
}Новини можна кешувати на 5 хвилин, а приватний профіль потрібно отримувати заново для кожного запиту.
Кеш завжди є компромісом між швидкістю та актуальністю:
довший час кешування — менше запитів і швидша відповідь;
коротший час кешування — свіжіші дані;
no-store — найактуальніші дані, але без переваг кешу.
Оберіть режим залежно від характеру даних:
каталог товарів можна оновлювати раз на кілька хвилин;
документацію можна кешувати на довший час;
баланс рахунку краще не кешувати;
персональні дані не слід бездумно зберігати у спільному кеші.
Для кешованих даних процес виглядає так:
Server Component викликає fetch.
Next.js перевіряє Data Cache.
Якщо актуальні дані є, повертається кешований результат.
Якщо даних немає або кеш застарів, виконується запит до API.
Новий результат зберігається в Data Cache.
Компонент відображає отримані дані.
no-store для всіх запитівЯкщо кожен запит має cache: 'no-store', Data Cache не приносить користі. Це може збільшити навантаження на API та уповільнити сторінку.
Використовуйте no-store лише для даних, яким справді потрібна актуальність під час кожного запиту.
Не слід кешувати персональну відповідь без розуміння того, як вона буде пов’язана з конкретним користувачем.
Для даних, що залежать від поточного користувача, часто потрібен cache: 'no-store'.
Якщо список має revalidate: 3600, він може залишатися в кеші до години. Після створення або редагування даних потрібно явно оновити відповідний тег чи маршрут.
revalidateTag і revalidatePath призначені для серверного коду. Їх не можна безпосередньо викликати у файлі з 'use client'.
response.okfetch не вважає HTTP-помилки на кшталт 404 або 500 винятками автоматично. Перевіряйте response.ok перед читанням відповіді.
Data Cache зберігає результати отримання даних на сервері.
cache: 'force-cache' явно вмикає кешування запиту.
next.revalidate задає час, після якого дані можна перевірити знову.
cache: 'no-store' повністю вимикає кешування для запиту.
Теги допомагають групувати пов’язані кешовані дані.
revalidateTag оновлює дані за тегом.
revalidatePath позначає конкретний маршрут для повторної перевірки.
Для публічних і стабільних даних кеш зазвичай покращує швидкість, а для приватних або дуже динамічних даних часто потрібен no-store.