Урок 02

Кеширование fetch

Next.js расширяет fetch возможностями кеширования.

По умолчанию

fetch в Server Components кешируется автоматически:

const res = await fetch('https://api.example.com/posts');

Результат сохраняется. Повторный запрос вернёт из кеша.

Отключить кеш

const res = await fetch('https://api.example.com/posts', {
    cache: 'no-store',
});

Каждый запрос идёт на сервер. Для динамических данных.

Принудительный кеш

const res = await fetch('https://api.example.com/posts', {
    cache: 'force-cache',
});

Даже если данные изменились — вернёт из кеша. Явное поведение по умолчанию.

Revalidate

Кеш на время:

const res = await fetch('https://api.example.com/posts', {
    next: { revalidate: 60 }, // 60 секунд
});

Через 60 секунд данные перезагрузятся.

revalidate: 0 — то же, что cache: 'no-store'.

revalidate: false — кеш навсегда.

Теги

Группировка запросов:

const res = await fetch('https://api.example.com/posts', {
    next: { tags: ['posts'] },
});

Перегенерация:

import { revalidateTag } from 'next/cache';

revalidateTag('posts');

Все запросы с тегом posts обновятся.

Несколько тегов:

next: { tags: ['posts', 'homepage'] }

revalidatePath

Обновить целую страницу:

import { revalidatePath } from 'next/cache';

revalidatePath('/posts');
revalidatePath('/posts/[id]', 'page'); // динамический

После мутации

'use server';

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

export async function createPost(data: FormData) {
    await db.post.create({ data: { title: data.get('title') } });

    revalidatePath('/posts');
    revalidateTag('posts');
}

Статическая vs динамическая

Статическая страница — генерируется при сборке, кешируется навсегда (до rebuild).

Динамическая — при каждом запросе. Если использовать cache: 'no-store', cookies(), headers(), searchParams — становится динамической.

Route Segment Config

Управление на уровне файла:

// app/posts/page.tsx
export const dynamic = 'force-dynamic'; // всегда динамическая
export const revalidate = 60;           // ISR на 60 секунд
export const fetchCache = 'force-no-store';

Пример: блог с ISR

export const revalidate = 3600; // 1 час

export default async function Blog() {
    const res = await fetch('https://api.example.com/posts', {
        next: { revalidate: 3600, tags: ['posts'] },
    });
    const posts = await res.json();

    return <PostList posts={posts} />;
}

Когда что

  • Статические данные (о компании) — force-cache, revalidate: false
  • Редко меняющиеся (блог, каталог) — ISR с revalidate: 3600
  • Часто меняющиеся (лента, комментарии) — revalidate: 60 или теги
  • Персональные (профиль, корзина) — no-store

Итоги

  • fetch кешируется по умолчанию
  • cache: 'no-store' для динамики
  • next: { revalidate: N } для ISR
  • next: { tags: [...] } для группировки
  • revalidateTag и revalidatePath после мутаций
  • Route Segment Config для уровня файла