Пошук уроків, статей та іншого контенту
Розділіть публічні та серверні змінні середовища й безпечно використовуйте їх у Next.js.
Змінні середовища — це значення конфігурації, які застосунок отримує з оточення, у якому він запускається. Наприклад:
URL зовнішнього API;
назва режиму роботи застосунку;
ключ доступу до бази даних;
секрет для підписування токенів.
У Next.js змінні середовища зберігають у файлах із назвою .env.
Змінні середовища не замінюють систему керування секретами для production, але допомагають не зберігати конфіденційні значення безпосередньо в коді.
Найчастіше використовують файл .env.local:
# .env.local
DATABASE_URL=postgresql://localhost:5432/shop
INTERNAL_API_KEY=local-secret-key
NEXT_PUBLIC_API_URL=https://api.example.comNext.js автоматично завантажує змінні з таких файлів:
.env — базові значення;
.env.local — локальні значення, які зазвичай не додають до Git;
.env.development — значення для режиму розробки;
.env.production — значення для production;
.env.test — значення для тестів.
Файли з суфіксом .local мають вищий пріоритет за відповідні звичайні файли. Наприклад, значення з .env.local може перевизначити значення з .env.
Файл .env.local зазвичай додають до .gitignore:
.env*.localТак секрети з локального середовища не потраплять у репозиторій.
У Next.js змінні середовища поділяються на два типи.
Змінна без префікса NEXT_PUBLIC_ доступна тільки на сервері:
DATABASE_URL=postgresql://localhost:5432/shop
INTERNAL_API_KEY=local-secret-keyТакі змінні можна використовувати:
у Server Components;
у Route Handlers;
у серверних функціях;
під час виконання іншого серверного коду.
Серверні змінні не можна передавати в браузер. До них належать паролі, приватні API-ключі та токени доступу.
Змінна з префіксом NEXT_PUBLIC_ може потрапити до JavaScript-коду браузера:
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_APP_NAME=ShopТакі змінні призначені лише для значень, які не є секретними:
публічна адреса API;
назва застосунку;
ідентифікатор, який дозволено бачити користувачам;
публічне налаштування функції.
Префікс NEXT_PUBLIC_ не робить значення безпечним. Навпаки, він повідомляє Next.js, що це значення можна вбудувати у клієнтський код.
Створимо мінімальну сторінку Next.js з App Router.
Файл .env.local:
INTERNAL_API_KEY=local-secret-key
NEXT_PUBLIC_API_URL=https://api.example.comФайл app/page.js:
export default function HomePage() {
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
const hasInternalKey = Boolean(process.env.INTERNAL_API_KEY);
return (
<main>
<h1>Налаштування застосунку</h1>
<p>Публічна адреса API: {apiUrl}</p>
<p>
Серверний ключ налаштовано: {hasInternalKey ? "так" : "ні"}
</p>
</main>
);
}За замовчуванням компонент app/page.js є Server Component. Тому він може прочитати як публічну, так і серверну змінну.
Значення INTERNAL_API_KEY не виводиться на сторінку. Ми лише перевіряємо, чи воно існує. Сам ключ залишається на сервері.
Клієнтський компонент може використовувати тільки змінні з префіксом NEXT_PUBLIC_.
Файл app/components/api-status.js:
"use client";
export default function ApiStatus() {
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
return <p>Клієнт використовує API: {apiUrl}</p>;
}Файл app/page.js:
import ApiStatus from "./components/api-status";
export default function HomePage() {
return (
<main>
<h1>Мій застосунок</h1>
<ApiStatus />
</main>
);
}NEXT_PUBLIC_API_URL можна використовувати в клієнтському компоненті, тому що вона є публічною.
Натомість цей код не повинен містити серверний секрет:
"use client";
export default function ApiStatus() {
// Так робити не можна: секрет не призначений для браузера
const secret = process.env.INTERNAL_API_KEY;
return <p>{secret}</p>;
}Змінна без NEXT_PUBLIC_ не повинна використовуватися в клієнтському компоненті. Навіть якщо значення не відобразилося очікуваним чином, не слід покладатися на таку поведінку для захисту секретів.
Якщо потрібно використати приватний ключ для запиту до зовнішнього сервісу, виконуйте запит на сервері.
Наприклад, файл app/api/products/route.js:
import { NextResponse } from "next/server";
export async function GET() {
const apiKey = process.env.INTERNAL_API_KEY;
if (!apiKey) {
return NextResponse.json(
{ error: "Серверний ключ не налаштовано" },
{ status: 500 }
);
}
const response = await fetch("https://api.example.com/products", {
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
if (!response.ok) {
return NextResponse.json(
{ error: "Не вдалося отримати товари" },
{ status: response.status }
);
}
const products = await response.json();
return NextResponse.json(products);
}У цьому прикладі браузер звертається до /api/products, а сервер Next.js сам додає приватний ключ до запиту. Ключ не потрібно передавати клієнту.
Схема роботи:
Браузер надсилає запит до /api/products.
Route Handler читає INTERNAL_API_KEY на сервері.
Route Handler звертається до зовнішнього API.
Сервер повертає браузеру лише результат запиту.
NEXT_PUBLIC_Публічні змінні зазвичай вбудовуються в клієнтський JavaScript під час збірки застосунку.
Тому після зміни .env.local потрібно перезапустити сервер розробки:
npm run devЯкщо застосунок уже було зібрано, зміна NEXT_PUBLIC_API_URL після збірки не обов’язково змінить значення у браузері. Для production потрібно виконати нову збірку із правильними змінними:
npm run build
npm run startЦе особливо важливо під час розгортання: значення публічних змінних має бути доступним у момент збірки.
Серверні змінні також потрібно налаштувати в середовищі, де працює сервер Next.js. Наприклад, у налаштуваннях платформи розгортання або в системі керування секретами.
Застосунок може завершитися з помилкою раніше, якщо необхідної змінної немає.
Створимо файл lib/env.js:
const requiredEnvironmentVariables = [
"INTERNAL_API_KEY",
"NEXT_PUBLIC_API_URL",
];
for (const variableName of requiredEnvironmentVariables) {
if (!process.env[variableName]) {
throw new Error(`Відсутня змінна середовища: ${variableName}`);
}
}
export const env = {
internalApiKey: process.env.INTERNAL_API_KEY,
publicApiUrl: process.env.NEXT_PUBLIC_API_URL,
};Тепер серверний код може використовувати перевірені значення:
import { env } from "@/lib/env";
export async function getProducts() {
const response = await fetch(`${env.publicApiUrl}/products`, {
headers: {
Authorization: `Bearer ${env.internalApiKey}`,
},
});
if (!response.ok) {
throw new Error("Не вдалося отримати товари");
}
return response.json();
}Такий підхід зменшує ризик отримати незрозумілу помилку в іншій частині застосунку. Якщо змінну не налаштовано, проблема буде помітною одразу.
Не додавайте .env.local до Git і не записуйте секрети безпосередньо в код:
const apiKey = "local-secret-key";Використовуйте змінну середовища:
const apiKey = process.env.INTERNAL_API_KEY;NEXT_PUBLIC_ для секретуНеправильно:
NEXT_PUBLIC_DATABASE_PASSWORD=secret-passwordУсе після NEXT_PUBLIC_ потенційно доступне браузеру. Для секретів використовуйте назви без цього префікса:
DATABASE_PASSWORD=secret-passwordКлієнтські компоненти виконуються у браузері. Вони не повинні напряму отримувати приватні змінні.
Замість цього:
використайте серверний компонент або Route Handler;
виконайте приватну операцію на сервері;
поверніть клієнту лише необхідні дані.
Після зміни .env.local перезапустіть npm run dev. Для production зберіть застосунок заново.
Навіть серверний код може випадково розкрити секрет:
return Response.json({
apiKey: process.env.INTERNAL_API_KEY,
});Серверна змінна має залишатися всередині серверної логіки. Повертайте тільки дані, які потрібні клієнту.
Файли .env* містять конфігурацію застосунку.
.env.local зазвичай використовують для локальних значень і не додають до Git.
Змінні без NEXT_PUBLIC_ призначені для сервера.
Змінні з NEXT_PUBLIC_ можуть потрапити до браузера і не повинні містити секретів.
Приватні ключі використовуйте в Server Components, Route Handlers та іншому серверному коді.
Після зміни змінних перезапускайте розробницький сервер, а для production виконуйте нову збірку.
Перевіряйте наявність обов’язкових змінних під час запуску застосунку.