Пошук уроків, статей та іншого контенту
Читайте, створюйте й видаляйте cookies у Server Components, Route Handlers і Server Actions.
Cookie — це невеликий фрагмент даних, який браузер зберігає для певного домену й автоматично надсилає разом із наступними HTTP-запитами.
Cookies часто використовують для:
ідентифікатора сесії;
токена автентифікації;
налаштувань користувача;
збереження короткочасного стану.
У Next.js App Router для роботи з cookies використовується функція cookies з next/headers:
import { cookies } from 'next/headers'cookies()awaitУ Server Component можна прочитати cookies, але не можна змінити відповідь браузера. Тому в Server Components дозволене лише читання.
// app/account/page.js
import { cookies } from 'next/headers'
export default async function AccountPage() {
const cookieStore = await cookies()
const session = cookieStore.get('session')
if (!session) {
return <p>Ви не авторизовані.</p>
}
return (
<main>
<h1>Особистий кабінет</h1>
<p>Ідентифікатор сесії: {session.value}</p>
</main>
)
}Метод get повертає об’єкт із назвою та значенням cookie або undefined, якщо cookie не існує:
const cookie = cookieStore.get('theme')
// cookie має вигляд:
// { name: 'theme', value: 'dark' }Для перевірки наявності cookie можна використати has:
const cookieStore = await cookies()
if (cookieStore.has('session')) {
// Cookie існує
}Щоб отримати всі cookies з однаковою назвою, використовується getAll:
const cookieStore = await cookies()
const cookiesWithSameName = cookieStore.getAll('tag')Також можна отримати всі доступні cookies:
const cookieStore = await cookies()
const allCookies = cookieStore.getAll()Route Handler може читати cookies запиту. Для цього можна використати cookies() або cookies об’єкта Request.
cookies()// app/api/profile/route.js
import { cookies } from 'next/headers'
export async function GET() {
const cookieStore = await cookies()
const session = cookieStore.get('session')
if (!session) {
return Response.json(
{ error: 'Сесію не знайдено' },
{ status: 401 }
)
}
return Response.json({
sessionId: session.value
})
}Request// app/api/profile/route.js
export async function GET(request) {
const session = request.cookies.get('session')
if (!session) {
return Response.json(
{ error: 'Сесію не знайдено' },
{ status: 401 }
)
}
return Response.json({
sessionId: session.value
})
}request.cookies.get('session') також повертає об’єкт із властивостями name і value.
Route Handler може змінювати cookies, оскільки формує HTTP-відповідь.
// app/api/preferences/route.js
import { cookies } from 'next/headers'
export async function POST() {
const cookieStore = await cookies()
cookieStore.set('theme', 'dark', {
httpOnly: true,
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 30,
path: '/'
})
return Response.json({
message: 'Налаштування збережено'
})
}Параметри cookie:
httpOnly — cookie не доступна через document.cookie у браузері;
secure — cookie надсилається лише через HTTPS;
sameSite — керує надсиланням cookie у міжсайтових запитах;
maxAge — час життя cookie у секундах;
expires — конкретна дата завершення дії;
path — шлях, для якого cookie доступна;
domain — домен, для якого cookie доступна.
Для сесій і токенів зазвичай варто використовувати httpOnly, щоб JavaScript у браузері не міг прочитати значення cookie.
secureможна вимикати в локальному HTTP-середовищі, але в production-середовищі його слід увімкнути.
NextResponseCookies також можна встановити на об’єкті NextResponse:
// app/api/preferences/route.js
import { NextResponse } from 'next/server'
export async function POST() {
const response = NextResponse.json({
message: 'Налаштування збережено'
})
response.cookies.set('theme', 'dark', {
httpOnly: true,
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 30,
path: '/'
})
return response
}Цей варіант зручний, коли потрібно явно працювати з HTTP-відповіддю.
Для видалення cookie використовується метод delete:
// app/api/preferences/route.js
import { cookies } from 'next/headers'
export async function DELETE() {
const cookieStore = await cookies()
cookieStore.delete('theme')
return Response.json({
message: 'Cookie видалено'
})
}Видалення фактично встановлює cookie з терміном дії в минулому. Щоб видалення спрацювало надійно, cookie повинна мати той самий домен і шлях, з якими її було створено.
Можна також явно встановити maxAge: 0:
const cookieStore = await cookies()
cookieStore.set('theme', '', {
maxAge: 0,
path: '/'
})Зазвичай простіше використовувати delete.
Server Action — це асинхронна серверна функція, яку можна викликати з форми або клієнтського коду.
Server Actions можуть:
читати cookies;
створювати cookies;
оновлювати cookies;
видаляти cookies.
// app/login/actions.js
'use server'
import { cookies } from 'next/headers'
import { redirect } from 'next/navigation'
export async function login(formData) {
const email = formData.get('email')
const password = formData.get('password')
if (
typeof email !== 'string' ||
typeof password !== 'string' ||
!email ||
!password
) {
throw new Error('Заповніть усі поля')
}
// У реальному застосунку тут виконується перевірка користувача.
const sessionId = 'session-example-123'
const cookieStore = await cookies()
cookieStore.set('session', sessionId, {
httpOnly: true,
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 7,
path: '/'
})
redirect('/account')
}Action отримує FormData, тому її можна підключити до атрибута action HTML-форми.
// app/login/page.js
import { login } from './actions'
export default function LoginPage() {
return (
<main>
<h1>Вхід</h1>
<form action={login}>
<label>
Електронна пошта
<input
type="email"
name="email"
required
/>
</label>
<label>
Пароль
<input
type="password"
name="password"
required
/>
</label>
<button type="submit">Увійти</button>
</form>
</main>
)
}Після надсилання форми Next.js викликає login на сервері. Action встановлює cookie та перенаправляє користувача на /account.
Наприклад, вихід із системи може бути окремою Server Action:
// app/account/actions.js
'use server'
import { cookies } from 'next/headers'
import { redirect } from 'next/navigation'
export async function logout() {
const cookieStore = await cookies()
cookieStore.delete('session')
redirect('/login')
}Використання цієї Action у Server Component:
// app/account/page.js
import { cookies } from 'next/headers'
import { logout } from './actions'
export default async function AccountPage() {
const cookieStore = await cookies()
const session = cookieStore.get('session')
if (!session) {
return <p>Ви не авторизовані.</p>
}
return (
<main>
<h1>Особистий кабінет</h1>
<form action={logout}>
<button type="submit">Вийти</button>
</form>
</main>
)
}| Контекст | Читання | Створення | Видалення | |---|---:|---:|---:| | Server Component | Так | Ні | Ні | | Route Handler | Так | Так | Так | | Server Action | Так | Так | Так |
Server Component формує інтерфейс на основі вхідного запиту, але не має окремої відповіді для зміни cookie. Route Handler і Server Action можуть змінювати cookies, тому що працюють у контексті HTTP-відповіді.
Cookie зберігається на стороні клієнта. Навіть якщо вона має прапорець httpOnly, її значення не слід вважати сховищем для довільних секретних даних.
Зазвичай у cookie зберігають:
випадковий ідентифікатор сесії;
короткочасний токен;
просте налаштування, наприклад тему оформлення.
Паролі та інші великі або критично важливі дані не слід зберігати в cookie.
httpOnly для сесійних cookiescookieStore.set('session', sessionId, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/'
})httpOnly забороняє доступ до cookie через JavaScript у браузері:
document.cookieЦе зменшує ризик викрадення сесійного cookie через шкідливий клієнтський скрипт.
pathЯкщо cookie повинна бути доступна в усьому застосунку, використовуйте:
path: '/'Якщо cookie створена з конкретним шляхом, під час видалення потрібно використати той самий шлях.
Виклик cookies() залежить від вхідного HTTP-запиту. Через це маршрут, який використовує cookies, не може бути повністю статично згенерований із наперед відомим результатом.
Наприклад, ця сторінка залежить від cookie конкретного користувача:
import { cookies } from 'next/headers'
export default async function DashboardPage() {
const cookieStore = await cookies()
const session = cookieStore.get('session')
return (
<main>
{session ? (
<p>Ви увійшли в систему.</p>
) : (
<p>Потрібно увійти.</p>
)}
</main>
)
}Вміст сторінки може відрізнятися для кожного запиту, тому Next.js враховує cookies під час її рендерингу.
Нижче наведено завершений Route Handler для читання, створення та видалення cookie theme.
// app/api/theme/route.js
import { cookies } from 'next/headers'
export async function GET() {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return Response.json({
theme: theme?.value ?? 'light'
})
}
export async function POST(request) {
const body = await request.json()
const theme = body.theme
if (theme !== 'light' && theme !== 'dark') {
return Response.json(
{ error: 'Непідтримувана тема' },
{ status: 400 }
)
}
const cookieStore = await cookies()
cookieStore.set('theme', theme, {
httpOnly: true,
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 30,
path: '/'
})
return Response.json({
theme
})
}
export async function DELETE() {
const cookieStore = await cookies()
cookieStore.delete('theme')
return Response.json({
message: 'Тему скинуто'
})
}Цей Route Handler доступний за адресою /api/theme:
GET /api/theme — прочитати тему;
POST /api/theme — встановити тему;
DELETE /api/theme — видалити тему.
// Неправильно
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
cookieStore.set('theme', 'dark')
return <p>Сторінка</p>
}Server Component призначений для читання cookies. Для зміни використовуйте Route Handler або Server Action.
await// Неправильно
const cookieStore = cookies()
const theme = cookieStore.get('theme')Правильний варіант:
const cookieStore = await cookies()
const theme = cookieStore.get('theme')httpOnly cookie у Client Component'use client'
document.cookieCookie з httpOnly: true недоступна клієнтському JavaScript. Це очікувана поведінка. Таку cookie потрібно читати на сервері — у Server Component, Route Handler або Server Action.
Якщо cookie створили так:
cookieStore.set('session', sessionId, {
path: '/account'
})то видалення з іншим шляхом може не спрацювати:
cookieStore.delete('session')У такому випадку важливо видаляти cookie з тим самим path, який використовувався під час створення.
Значення cookie має бути рядком. Якщо потрібно зберегти кілька простих значень, їх можна серіалізувати:
const preferences = {
theme: 'dark',
language: 'uk'
}
cookieStore.set(
'preferences',
JSON.stringify(preferences),
{
httpOnly: true,
path: '/'
}
)Під час читання значення потрібно розпарсити та перевірити:
const cookieStore = await cookies()
const preferencesCookie = cookieStore.get('preferences')
let preferences = null
if (preferencesCookie) {
try {
preferences = JSON.parse(preferencesCookie.value)
} catch {
preferences = null
}
}Для роботи з cookies у Next.js використовується cookies з next/headers.
У сучасних версіях Next.js потрібно писати await cookies().
Server Components можуть читати cookies, але не можуть їх змінювати.
Route Handlers і Server Actions можуть читати, створювати, оновлювати та видаляти cookies.
Для сесійних cookies зазвичай використовують httpOnly, secure, sameSite і path.
Для видалення cookie використовується delete.
Під час видалення важливо враховувати той самий домен і шлях, з якими cookie була створена.