Перейти к содержимому

24. Деплой на Vercel

Ты написал крутое приложение — теперь пора показать его миру! И тут на сцену выходит Vercel — платформа, которая превращает деплой из боли в удовольствие. Буквально: git push → приложение в интернете. Магия? Почти! 🎩✨


Представь, что ты испёк торт (твоё Next.js-приложение). Тебе нужно его доставить гостям. Обычный хостинг — это как самостоятельно арендовать грузовик, купить топливо, найти водителя и прокладывать маршрут. Vercel — это DHL: ты просто отдаёшь торт, они сами доставят его в любую точку мира быстро и свежим.

Vercel — это облачная платформа, созданная той же командой, что создала Next.js. Они знают фреймворк изнутри, поэтому интеграция идеальная:

Обычный VPS-хостинг │ Vercel
────────────────────────────┼──────────────────────────────────
Настраиваешь сервер вручную │ Zero-config, всё работает само
Один регион │ Edge Network в 100+ регионах
CI/CD — сам настраиваешь │ Встроен по умолчанию
SSL — сам получаешь │ Автоматический HTTPS
Preview-окружения — вручную │ Каждый PR получает URL
Мониторинг — сторонние │ Vercel Analytics встроен
Откат — сам разбирайся │ One-click rollback

Vercel поддерживает не только Next.js, но и Nuxt, SvelteKit, Astro, Remix и даже статические сайты. Но для Next.js он как родной дом — поддержка всех фич из коробки.


Это не маркетинговый слоган, это реальность. Процесс:

  1. Подключаешь репозиторий к Vercel (один раз)
  2. Делаешь git push
  3. Vercel автоматически обнаруживает Next.js
  4. Запускает npm run build
  5. Деплоит на CDN по всему миру
  6. Ты получаешь URL вида your-app.vercel.app

Всё. Никаких конфигов, никаких YAML-файлов, никакой настройки сервера.

Окно терминала
git add .
git commit -m "feat: добавил крутую фичу"
git push origin main
# Через 30-60 секунд приложение обновлено в продакшне 🎉

Шаг 1: Идёшь на vercel.com и входишь через GitHub/GitLab/Bitbucket.

Шаг 2: Нажимаешь “Add New Project” → выбираешь репозиторий.

Шаг 3: Vercel автоматически определяет Next.js и предлагает настройки:

Framework Preset: Next.js ✅ автоопределено
Root Directory: ./ (можно изменить для monorepo)
Build Command: next build ✅ автоопределено
Output Directory: .next ✅ автоопределено
Install Command: npm install ✅ автоопределено

Шаг 4: Жмёшь “Deploy” — и готово! 🚀

Для монорепо или нестандартных структур можно указать другой Root Directory. Например, если Next.js лежит в apps/web, укажи apps/web как корневую директорию.


Для большинства проектов vercel.json не нужен. Но когда нужна тонкая настройка — он тут как тут:

{
"framework": "nextjs",
"regions": ["fra1", "iad1"],
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "no-store" },
{ "key": "X-Content-Type-Options", "value": "nosniff" }
]
},
{
"source": "/(.*)",
"headers": [
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "X-XSS-Protection", "value": "1; mode=block" }
]
}
],
"redirects": [
{
"source": "/old-blog/:slug",
"destination": "/blog/:slug",
"permanent": true
}
],
"rewrites": [
{
"source": "/api/external/:path*",
"destination": "https://api.external-service.com/:path*"
}
],
"buildCommand": "npm run build",
"installCommand": "npm ci",
"ignoreCommand": "git diff HEAD^ HEAD --quiet -- . ':(exclude)README.md'"
}

Поле ignoreCommand — особое: если команда возвращает код 0, деплой пропускается. Полезно, когда хочешь деплоить только при реальных изменениях кода.


Переменные окружения — это секреты и настройки, которые не должны попасть в репозиторий. В Vercel есть удобный интерфейс:

Где настраивать: Project → Settings → Environment Variables

Окно терминала
# Типичный набор переменных для Next.js-проекта:
DATABASE_URL=postgresql://user:pass@host:5432/db
NEXTAUTH_SECRET=super-secret-random-string-32chars
NEXTAUTH_URL=https://your-app.vercel.app
STRIPE_SECRET_KEY=sk_live_...
STRIPE_PUBLISHABLE_KEY=pk_live_...
RESEND_API_KEY=re_...

В Vercel переменные можно назначать для конкретных окружений:

🔴 Production — только для main ветки (реальные ключи)
🟡 Preview — для PR и feature-веток (тестовые ключи)
🔵 Development — для локальной разработки (vercel env pull)

Скачать переменные для локальной разработки:

Окно терминала
# Устанавливаем Vercel CLI
npm install -g vercel
# Логинимся
vercel login
# Связываем локальный проект с Vercel-проектом
vercel link
# Скачиваем переменные окружения
vercel env pull .env.local
# Теперь .env.local содержит все переменные из Vercel Dashboard

Важно: Переменные с префиксом NEXT_PUBLIC_ доступны на клиенте. Все остальные — только на сервере. Никогда не добавляй NEXT_PUBLIC_ к секретным ключам!

app/api/payment/route.ts
// ✅ Правильно — секретные ключи только на сервере
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// ✅ Правильно — публичный ключ можно на клиенте
// components/PaymentForm.tsx (клиентский)
const stripe = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
// ❌ Опасно — секретный ключ в клиентском компоненте!
// process.env.STRIPE_SECRET_KEY — будет undefined на клиенте,
// но если добавишь NEXT_PUBLIC_ — утечёт в браузер!

Это одна из суперсил Vercel. Когда ты создаёшь Pull Request, Vercel автоматически:

  1. Собирает твою ветку
  2. Деплоит в изолированное окружение
  3. Добавляет комментарий в PR с URL вида your-app-git-feature-name.vercel.app
main branch → your-app.vercel.app (Production)
feature/auth → your-app-git-feature-auth.vercel.app (Preview)
fix/bug-123 → your-app-git-fix-bug-123.vercel.app (Preview)

Это даёт команде возможность:

  • Проверить изменения до мержа в main
  • Показать заказчику конкретную фичу по ссылке
  • Запускать E2E-тесты на реальном деплое
  • Дизайнеры могут проверить верстку без доступа к коду
.github/workflows/e2e.yml
name: E2E Tests on Preview
on:
deployment_status:
jobs:
cypress:
if: github.event.deployment_status.state == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Cypress
uses: cypress-io/github-action@v6
with:
config: baseUrl=${{ github.event.deployment_status.target_url }}

// Определяем окружение в коде
const isProduction = process.env.VERCEL_ENV === 'production';
const isPreview = process.env.VERCEL_ENV === 'preview';
const isDevelopment = process.env.VERCEL_ENV === 'development';
// Vercel автоматически устанавливает эти переменные:
// VERCEL_ENV — 'production' | 'preview' | 'development'
// VERCEL_URL — текущий URL деплоя (без https://)
// VERCEL_GIT_COMMIT_SHA — SHA коммита
// VERCEL_GIT_COMMIT_MESSAGE — сообщение коммита
// VERCEL_GIT_BRANCH — ветка
// Использование:
const apiUrl = isProduction
? 'https://api.my-app.com'
: `https://${process.env.VERCEL_URL}/api`;
// app/layout.tsx — аналитика только в продакшне
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
{children}
{isProduction && <Analytics />}
{isProduction && <SpeedInsights />}
</body>
</html>
);
}

Edge Functions выполняются на серверах, ближайших к пользователю. Это значит — минимальная задержка, максимальная скорость. Но с ограничениями: нет Node.js API, нет файловой системы, только Web APIs.

// app/api/geo/route.ts — Edge Function
export const runtime = 'edge';
export async function GET(request: Request) {
// Vercel добавляет геолокацию в заголовки
const country = request.headers.get('x-vercel-ip-country') || 'Unknown';
const city = request.headers.get('x-vercel-ip-city') || 'Unknown';
const region = request.headers.get('x-vercel-ip-country-region') || 'Unknown';
return Response.json({
country,
city,
region,
message: `Привет из ${city}, ${country}! 👋`,
});
}
// middleware.ts — тоже Edge Runtime
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const country = request.geo?.country || 'US';
// Редирект русскоязычных пользователей на /ru
if (country === 'RU' || country === 'BY' || country === 'KZ') {
if (!request.nextUrl.pathname.startsWith('/ru')) {
return NextResponse.redirect(new URL('/ru' + request.nextUrl.pathname, request.url));
}
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

Vercel Analytics — это privacy-friendly аналитика. Никаких cookies, никаких GDPR-согласий!

Окно терминала
npm install @vercel/analytics
app/layout.tsx
import { Analytics } from '@vercel/analytics/react';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ru">
<body>
{children}
<Analytics />
</body>
</html>
);
}

Что показывает Vercel Analytics:

  • Уникальные посетители и просмотры страниц
  • Источники трафика (откуда пришли)
  • Самые популярные страницы
  • Устройства и браузеры
  • Страны пользователей

Данные собираются через API без cookies — это законно в ЕС без баннеров!


Speed Insights показывает реальные данные производительности от твоих пользователей:

Окно терминала
npm install @vercel/speed-insights
app/layout.tsx
import { Analytics } from '@vercel/analytics/react';
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ru">
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
);
}

Метрики, которые отслеживает Speed Insights:

LCP (Largest Contentful Paint) — загрузка главного контента
✅ Хорошо: < 2.5с
⚠️ Нужно улучшить: 2.5-4с
❌ Плохо: > 4с
FID (First Input Delay) — отзывчивость на первое действие
✅ Хорошо: < 100мс
⚠️ Нужно улучшить: 100-300мс
❌ Плохо: > 300мс
CLS (Cumulative Layout Shift) — стабильность макета
✅ Хорошо: < 0.1
⚠️ Нужно улучшить: 0.1-0.25
❌ Плохо: > 0.25
INP (Interaction to Next Paint) — замена FID с 2024
✅ Хорошо: < 200мс
⚠️ Нужно улучшить: 200-500мс
❌ Плохо: > 500мс

Vercel Blob — это хранилище файлов, интегрированное прямо в экосистему Vercel:

Окно терминала
npm install @vercel/blob
app/api/upload/route.ts
import { put } from '@vercel/blob';
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
const form = await request.formData();
const file = form.get('file') as File;
if (!file) {
return NextResponse.json({ error: 'Файл не найден' }, { status: 400 });
}
const blob = await put(file.name, file, {
access: 'public',
contentType: file.type,
});
return NextResponse.json({
url: blob.url,
size: blob.size,
contentType: blob.contentType,
});
}
// Получение и удаление файлов
import { list, del } from '@vercel/blob';
// Список файлов
const { blobs } = await list({ prefix: 'avatars/' });
// Удаление файла
await del('https://your-blob-url.vercel-storage.com/file.jpg');

Vercel KV — это managed Redis-совместимое хранилище. Идеально для кэширования, сессий, rate limiting:

Окно терминала
npm install @vercel/kv
lib/kv.ts
import { kv } from '@vercel/kv';
// Rate limiting — не больше 10 запросов в минуту
export async function rateLimit(ip: string): Promise<boolean> {
const key = `rate_limit:${ip}`;
const current = await kv.incr(key);
if (current === 1) {
await kv.expire(key, 60); // сбрасывается каждые 60 секунд
}
return current <= 10;
}
// Кэширование данных
export async function getCachedUser(userId: string) {
const cached = await kv.get(`user:${userId}`);
if (cached) return cached;
const user = await fetchUserFromDB(userId);
await kv.set(`user:${userId}`, user, { ex: 3600 }); // 1 час
return user;
}
// Счётчики и метрики
export async function trackPageView(page: string) {
await kv.hincrby('page_views', page, 1);
}
export async function getPageViews() {
return kv.hgetall('page_views');
}

Окно терминала
npm install @vercel/postgres
lib/db.ts
import { sql } from '@vercel/postgres';
export async function getUsers() {
const { rows } = await sql`
SELECT id, name, email, created_at
FROM users
ORDER BY created_at DESC
LIMIT 10
`;
return rows;
}
export async function createUser(name: string, email: string) {
const { rows } = await sql`
INSERT INTO users (name, email)
VALUES (${name}, ${email})
RETURNING *
`;
return rows[0];
}
// С Drizzle ORM (рекомендуется)
import { drizzle } from 'drizzle-orm/vercel-postgres';
import { sql as vercelSql } from '@vercel/postgres';
export const db = drizzle(vercelSql);

Добавить свой домен в Vercel очень просто:

Project → Settings → Domains → Add

Введи: my-awesome-app.com

Vercel покажет DNS-записи для настройки:

# Вариант 1: CNAME (для поддоменов)
www.my-awesome-app.com CNAME cname.vercel-dns.com
# Вариант 2: A-записи (для apex-домена)
my-awesome-app.com A 76.76.21.21
# Вариант 3: Nameservers (Vercel управляет всем DNS)
ns1.vercel-dns.com
ns2.vercel-dns.com

Vercel автоматически выпустит SSL-сертификат через Let’s Encrypt и настроит HTTPS. Без единой команды!


Что-то пошло не так? Откатиться — одна кнопка:

Project → Deployments → (найди нужный деплой) → … → Promote to Production

Или через CLI:

Окно терминала
# Просмотр всех деплоев
vercel ls
# Откат к предыдущему деплою
vercel rollback [deployment-url]
# Пример:
vercel rollback https://my-app-abc123.vercel.app

Vercel хранит все деплои — можно откатиться к любому моменту истории!


Окно терминала
# Установка
npm install -g vercel
# Авторизация
vercel login
# Связывание проекта
vercel link
# Локальная разработка с Vercel-функциями
vercel dev
# Эмулирует Edge Functions, Serverless Functions, переменные окружения
# Деплой в preview
vercel
# Деплой в production
vercel --prod
# Управление переменными окружения
vercel env add DATABASE_URL # Добавить переменную
vercel env ls # Список переменных
vercel env rm DATABASE_URL # Удалить переменную
vercel env pull .env.local # Скачать в файл
# Просмотр логов
vercel logs [deployment-url]
vercel logs --follow # Логи в реальном времени
# Управление доменами
vercel domains ls
vercel domains add my-domain.com
vercel domains rm my-domain.com
# Алиасы деплоев
vercel alias set [deployment-url] my-app.vercel.app

.github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run type check
run: npm run type-check
- name: Run tests
run: npm run test
- name: Run linting
run: npm run lint
# Vercel автоматически деплоит после успешного прохождения тестов!
# Настраивается в: Project → Settings → Git → Deployment Protection

// Определение текущего URL с учётом окружения
export function getBaseUrl() {
if (typeof window !== 'undefined') return ''; // Браузер
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // Vercel
return 'http://localhost:3000'; // Локально
}
// Генерация метаданных с правильным URL
export function generateMetadata(): Metadata {
const baseUrl = getBaseUrl();
return {
metadataBase: new URL(baseUrl),
openGraph: {
url: baseUrl,
siteName: 'My Next.js App',
},
};
}
app/api/revalidate/route.ts
// Revalidation по webhook от CMS
import { revalidatePath, revalidateTag } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const secret = request.nextUrl.searchParams.get('secret');
if (secret !== process.env.REVALIDATION_SECRET) {
return NextResponse.json({ error: 'Invalid secret' }, { status: 401 });
}
const { path, tag } = await request.json();
if (path) revalidatePath(path);
if (tag) revalidateTag(tag);
return NextResponse.json({ revalidated: true, now: Date.now() });
}

🎯 Итог: почему Vercel — лучший выбор для Next.js

Заголовок раздела «🎯 Итог: почему Vercel — лучший выбор для Next.js»
ФичаVercelNetlifyRailwayVPS
Next.js поддержка✅✅ Родная✅ Хорошая✅ Есть⚠️ Сам настраивай
Edge Functions✅ 100+ регионов✅ Есть
Preview Deployments
Analytics✅ Встроен⚠️ Плагин
Managed DB✅ Postgres/KV/Blob
Бесплатный план✅ Щедрый✅ Есть⚠️ Лимиты
Кастомные домены
Цена на старте$0$0$5/мес$5/мес

Для большинства Next.js-проектов Vercel — это идеальное решение. Быстрый старт, масштабирование без боли, всё интегрировано.