Next.js Server API: headers, cookies, cache
Next.js App Router предоставляет набор серверных и клиентских API для работы с запросом, навигацией и кэшированием. Правильный выбор между headers(), cookies(), redirect() и хуками useRouter() определяет архитектуру страницы и её производительность. В статье разберём синтаксис, примеры и ограничения каждой функции, а также соберём их в сводную таблицу.
Содержание
- next/headers
- next/navigation
- next/cache
- Клиентские хуки
- Сводная таблица
- Лучшие практики
- Динамический рендеринг
- Ключевые тезисы для интервью
- Заключение
- Полезные ссылки
next/headers
headers()
Назначение: Чтение HTTP заголовков входящего запроса в Server Components.
Импорт:
import { headers } from 'next/headers'
Пример использования:
import { headers } from 'next/headers'
export default async function Page() {
const headersList = await headers()
const userAgent = headersList.get('user-agent')
const authorization = headersList.get('authorization')
return <div>User Agent: {userAgent}</div>
}
Важно:
- Асинхронная функция (начиная с Next.js 15)
- Возвращает read-only объект Headers
- Нельзя изменять заголовки (нет методов
setилиdelete) - Использование делает маршрут динамическим (отключает статическую генерацию)
Доступные методы:
get(name)- получить значение заголовкаhas(name)- проверить наличие заголовкаentries()- итератор всех пар ключ/значениеkeys()- итератор ключейvalues()- итератор значенийforEach()- выполнить функцию для каждой пары
cookies()
Назначение: Чтение и запись HTTP cookies в Server Components, Server Functions и Route Handlers.
Импорт:
import { cookies } from 'next/headers'
Примеры:
Чтение cookie:
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return <div>Current theme: {theme?.value}</div>
}
Запись cookie (только в Server Functions или Route Handlers):
'use server'
import { cookies } from 'next/headers'
export async function setTheme(theme: string) {
const cookieStore = await cookies()
cookieStore.set('theme', theme, {
secure: true,
httpOnly: true,
maxAge: 60 * 60 * 24 * 30 // 30 дней
})
}
Удаление cookie:
'use server'
import { cookies } from 'next/headers'
export async function deleteTheme() {
const cookieStore = await cookies()
cookieStore.delete('theme')
}
Важно:
- Асинхронная функция (начиная с Next.js 15)
- В Server Components можно только читать cookies
- В Server Functions и Route Handlers можно читать и писать
- Использование делает маршрут динамическим
Доступные методы:
get(name)- получить cookie по имениgetAll()- получить все cookieshas(name)- проверить наличие cookieset(name, value, options)- установить cookiedelete(name)- удалить cookie
Опции при установке cookie:
expires- дата истеченияmaxAge- время жизни в секундахdomain- доменpath- путь (по умолчанию/)secure- только HTTPShttpOnly- недоступна для JavaScriptsameSite- контроль кросс-доменных запросов (lax,strict,none)
draftMode()
Назначение: Управление режимом черновика для предпросмотра неопубликованного контента из CMS.
Импорт:
import { draftMode } from 'next/headers'
Примеры:
Проверка статуса:
import { draftMode } from 'next/headers'
export default async function Page() {
const { isEnabled } = await draftMode()
return (
<div>
Draft Mode is {isEnabled ? 'Enabled' : 'Disabled'}
</div>
)
}
Включение режима (в Route Handler):
import { draftMode } from 'next/headers'
export async function GET() {
const draft = await draftMode()
draft.enable()
return new Response('Draft mode enabled')
}
Выключение режима:
import { draftMode } from 'next/headers'
export async function GET() {
const draft = await draftMode()
draft.disable()
return new Response('Draft mode disabled')
}
Важно:
- Асинхронная функция (начиная с Next.js 15)
- Работает через cookie
__prerender_bypass - При включении обходит кэширование
- Новое значение cookie генерируется при каждом
next build
next/navigation
redirect()
Назначение: Перенаправление пользователя на другой URL.
Импорт:
import { redirect } from 'next/navigation'
Пример:
import { redirect } from 'next/navigation'
export default async function Page({ params }) {
const { id } = await params
const data = await fetchData(id)
if (!data) {
redirect('/login')
}
return <div>{data.title}</div>
}
Параметры:
redirect(path: string, type?: 'replace' | 'push')
Важно:
- Бросает ошибку
NEXT_REDIRECT- вызывайте вне блокаtry/catch - В Server Actions использует 303 redirect
- В остальных случаях использует 307 redirect
- Поддерживает абсолютные URL для внешних ссылок
- В Client Components работает только при рендеринге, не в обработчиках событий
Альтернатива: permanentRedirect() для 308 redirect
notFound()
Назначение: Отображение страницы 404 (not-found).
Импорт:
import { notFound } from 'next/navigation'
Пример:
import { notFound } from 'next/navigation'
export default async function Page({ params }) {
const { id } = await params
const user = await fetchUser(id)
if (!user) {
notFound()
}
return <div>{user.name}</div>
}
Важно:
- Бросает ошибку
NEXT_HTTP_ERROR_FALLBACK;404 - Автоматически добавляет
<meta name="robots" content="noindex" /> - Рендерит файл
not-found.tsxв текущем сегменте маршрута - Не требует
returnблагодаря TypeScript типуnever
useRouter()
Назначение: Программная навигация в Client Components.
Импорт:
import { useRouter } from 'next/navigation'
Пример:
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button onClick={() => router.push('/dashboard')}>
Go to Dashboard
</button>
)
}
Доступные методы:
push(href, options)- навигация с добавлением в историюreplace(href, options)- навигация без добавления в историюrefresh()- обновление текущей страницыprefetch(href)- предварительная загрузка маршрутаback()- назадforward()- вперед
Важно:
- Работает только в Client Components
- Для навигации предпочитайте
<Link>компонент - Опция
scroll: falseотключает прокрутку вверх
next/cache
revalidatePath()
Назначение: Инвалидация кэшированных данных для конкретного пути.
Импорт:
import { revalidatePath } from 'next/cache'
Примеры:
Инвалидация конкретной страницы:
import { revalidatePath } from 'next/cache'
revalidatePath('/blog/post-1')
Инвалидация всех страниц с динамическим сегментом:
import { revalidatePath } from 'next/cache'
revalidatePath('/blog/[slug]', 'page')
Инвалидация layout:
import { revalidatePath } from 'next/cache'
revalidatePath('/blog/[slug]', 'layout')
Использование в Server Action:
'use server'
import { revalidatePath } from 'next/cache'
export async function updatePost() {
await savePost()
revalidatePath('/blog')
}
Параметры:
revalidatePath(path: string, type?: 'page' | 'layout')
Важно:
- Работает только в Server Functions и Route Handlers
- При использовании с rewrites указывайте destination path
- Инвалидация происходит при следующем посещении пути
revalidateTag()
Назначение: Инвалидация кэшированных данных по тегам.
Импорт:
import { revalidateTag } from 'next/cache'
Пример:
Добавление тега к fetch:
const posts = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] }
})
Инвалидация по тегу:
'use server'
import { revalidateTag } from 'next/cache'
export async function addPost() {
await savePost()
revalidateTag('posts', 'max')
}
Параметры:
revalidateTag(tag: string, profile?: string | { expire?: number })
Важно:
- Рекомендуется использовать
profile: 'max'для stale-while-revalidate - Инвалидирует данные во всех страницах, использующих этот тег
- Работает только в Server Functions и Route Handlers
Клиентские хуки
usePathname()
Назначение: Получение текущего пути URL в Client Components.
'use client'
import { usePathname } from 'next/navigation'
export default function Page() {
const pathname = usePathname()
return <div>Current path: {pathname}</div>
}
useSearchParams()
Назначение: Получение параметров поиска (query string) в Client Components.
'use client'
import { useSearchParams } from 'next/navigation'
export default function Page() {
const searchParams = useSearchParams()
const query = searchParams.get('q')
return <div>Search: {query}</div>
}
Важно: Вызывает клиентский рендеринг до ближайшего Suspense boundary
Сводная таблица
| API | Импорт | Где работает | Назначение |
|---|---|---|---|
headers() | next/headers | Server Components | Чтение HTTP заголовков |
cookies() | next/headers | Server Components, Server Functions | Работа с cookies |
draftMode() | next/headers | Server Components, Route Handlers | Управление режимом черновика |
redirect() | next/navigation | Server/Client Components, Server Actions | Перенаправление |
notFound() | next/navigation | Server/Client Components | Отображение 404 |
useRouter() | next/navigation | Client Components | Программная навигация |
usePathname() | next/navigation | Client Components | Текущий путь |
useSearchParams() | next/navigation | Client Components | Параметры поиска |
revalidatePath() | next/cache | Server Functions, Route Handlers | Инвалидация кэша по пути |
revalidateTag() | next/cache | Server Functions, Route Handlers | Инвалидация кэша по тегам |
Лучшие практики
1. Работа с cookies
// ✅ Правильно: чтение в Server Component
export default async function Page() {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return <div>{theme?.value}</div>
}
// ✅ Правильно: запись в Server Action
'use server'
export async function updateTheme(theme: string) {
const cookieStore = await cookies()
cookieStore.set('theme', theme)
}
// ❌ Неправильно: попытка записи в Server Component
export default async function Page() {
const cookieStore = await cookies()
cookieStore.set('theme', 'dark') // Ошибка!
}
2. Обработка ошибок с redirect
// ✅ Правильно: redirect вне try/catch
export default async function Page({ params }) {
const { id } = await params
try {
const data = await fetchData(id)
if (!data) redirect('/not-found')
return <div>{data.title}</div>
} catch (error) {
// Обработка других ошибок
console.error(error)
return <div>Error occurred</div>
}
}
// ❌ Неправильно: redirect внутри try/catch
export default async function Page({ params }) {
const { id } = await params
try {
const data = await fetchData(id)
if (!data) redirect('/not-found') // Будет пойман!
} catch (error) {
// redirect бросает ошибку, которая будет поймана здесь
}
}
3. Комбинирование revalidatePath и revalidateTag
'use server'
import { revalidatePath, revalidateTag } from 'next/cache'
export async function updatePost(id: string) {
await savePost(id)
// Инвалидация конкретной страницы
revalidatePath(`/blog/${id}`)
// Инвалидация всех страниц с постами
revalidateTag('posts', 'max')
}
4. Использование headers для авторизации
import { headers } from 'next/headers'
export default async function Page() {
const headersList = await headers()
const authorization = headersList.get('authorization')
const res = await fetch('https://api.example.com/user', {
headers: { authorization }
})
const user = await res.json()
return <div>{user.name}</div>
}
Динамический рендеринг
Использование следующих API автоматически делает маршрут динамическим:
headers()cookies()draftMode()searchParamsв page components
Это означает, что страница будет рендериться при каждом запросе, а не кэшироваться статически.
Ключевые тезисы для интервью
headers()изnext/headersвозвращает read-only объект HTTP-заголовков и делает маршрут динамическим.cookies()позволяет читать в Server Components, а записывать — только в Server Functions и Route Handlers.draftMode()управляет режимом черновика через cookie__prerender_bypassи обходит кэширование.redirect()бросает ошибкуNEXT_REDIRECT, поэтому его нельзя вызывать внутриtry/catch.notFound()прерывает рендеринг и автоматически отображаетnot-found.tsxсnoindex.useRouter()работает только в Client Components и предоставляет методыpush,replace,refresh,prefetch,back,forward.useSearchParams()иusePathname()возвращают клиентские данные URL и требуют ближайшего Suspense boundary.revalidatePath()инвалидирует кэш по конкретному пути или layout, аrevalidateTag()— по тегам fetch.headers(),cookies(),draftMode()иsearchParamsв page-компонентах автоматически переключают маршрут в динамический рендеринг.- Для cookies важно разделять чтение в Server Components и запись в Server Actions, иначе возникнет ошибка.
Заключение
Next.js App Router даёт чёткое разделение между серверными и клиентскими API: headers(), cookies() и draftMode() работают на сервере, а useRouter(), usePathname() и useSearchParams() — только в клиентских компонентах. Понимание этих границ помогает избежать ошибок при записи cookies и вызове redirect(). Правильное использование revalidatePath() и revalidateTag() позволяет эффективно управлять кэшем без лишнего рендеринга. Попробуйте перенести запись cookies из Server Components в Server Actions и проверьте, какие маршруты становятся динамическими.