24. Деплой на Vercel
🚀 Деплой Next.js на Vercel
Заголовок раздела «🚀 Деплой Next.js на Vercel»Ты написал крутое приложение — теперь пора показать его миру! И тут на сцену выходит Vercel — платформа, которая превращает деплой из боли в удовольствие. Буквально: git push → приложение в интернете. Магия? Почти! 🎩✨
🏠 Что такое Vercel?
Заголовок раздела «🏠 Что такое Vercel?»Представь, что ты испёк торт (твоё Next.js-приложение). Тебе нужно его доставить гостям. Обычный хостинг — это как самостоятельно арендовать грузовик, купить топливо, найти водителя и прокладывать маршрут. Vercel — это DHL: ты просто отдаёшь торт, они сами доставят его в любую точку мира быстро и свежим.
Vercel — это облачная платформа, созданная той же командой, что создала Next.js. Они знают фреймворк изнутри, поэтому интеграция идеальная:
Обычный VPS-хостинг │ Vercel────────────────────────────┼──────────────────────────────────Настраиваешь сервер вручную │ Zero-config, всё работает самоОдин регион │ Edge Network в 100+ регионахCI/CD — сам настраиваешь │ Встроен по умолчаниюSSL — сам получаешь │ Автоматический HTTPSPreview-окружения — вручную │ Каждый PR получает URLМониторинг — сторонние │ Vercel Analytics встроенОткат — сам разбирайся │ One-click rollbackVercel поддерживает не только Next.js, но и Nuxt, SvelteKit, Astro, Remix и даже статические сайты. Но для Next.js он как родной дом — поддержка всех фич из коробки.
⚡ Zero-config деплой: git push → сайт в интернете
Заголовок раздела «⚡ Zero-config деплой: git push → сайт в интернете»Это не маркетинговый слоган, это реальность. Процесс:
- Подключаешь репозиторий к Vercel (один раз)
- Делаешь
git push - Vercel автоматически обнаруживает Next.js
- Запускает
npm run build - Деплоит на CDN по всему миру
- Ты получаешь URL вида
your-app.vercel.app
Всё. Никаких конфигов, никаких YAML-файлов, никакой настройки сервера.
git add .git commit -m "feat: добавил крутую фичу"git push origin main
# Через 30-60 секунд приложение обновлено в продакшне 🎉🔗 Подключение репозитория к Vercel
Заголовок раздела «🔗 Подключение репозитория к Vercel»Шаг 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 — тонкая настройка
Заголовок раздела «📄 vercel.json — тонкая настройка»Для большинства проектов 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 Dashboard
Заголовок раздела «🔐 Переменные окружения в Vercel Dashboard»Переменные окружения — это секреты и настройки, которые не должны попасть в репозиторий. В Vercel есть удобный интерфейс:
Где настраивать: Project → Settings → Environment Variables
# Типичный набор переменных для Next.js-проекта:DATABASE_URL=postgresql://user:pass@host:5432/dbNEXTAUTH_SECRET=super-secret-random-string-32charsNEXTAUTH_URL=https://your-app.vercel.appSTRIPE_SECRET_KEY=sk_live_...STRIPE_PUBLISHABLE_KEY=pk_live_...RESEND_API_KEY=re_...В Vercel переменные можно назначать для конкретных окружений:
🔴 Production — только для main ветки (реальные ключи)🟡 Preview — для PR и feature-веток (тестовые ключи)🔵 Development — для локальной разработки (vercel env pull)Скачать переменные для локальной разработки:
# Устанавливаем Vercel CLInpm install -g vercel
# Логинимсяvercel login
# Связываем локальный проект с Vercel-проектомvercel link
# Скачиваем переменные окруженияvercel env pull .env.local
# Теперь .env.local содержит все переменные из Vercel DashboardВажно: Переменные с префиксом NEXT_PUBLIC_ доступны на клиенте. Все остальные — только на сервере. Никогда не добавляй NEXT_PUBLIC_ к секретным ключам!
// ✅ Правильно — секретные ключи только на сервере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_ — утечёт в браузер!🌿 Preview Deployments — каждый PR получает URL
Заголовок раздела «🌿 Preview Deployments — каждый PR получает URL»Это одна из суперсил Vercel. Когда ты создаёшь Pull Request, Vercel автоматически:
- Собирает твою ветку
- Деплоит в изолированное окружение
- Добавляет комментарий в 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-тесты на реальном деплое
- Дизайнеры могут проверить верстку без доступа к коду
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 }}🏭 Production vs Preview environments
Заголовок раздела «🏭 Production vs Preview environments»// Определяем окружение в коде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 на Vercel
Заголовок раздела «⚡ Edge Functions на Vercel»Edge Functions выполняются на серверах, ближайших к пользователю. Это значит — минимальная задержка, максимальная скорость. Но с ограничениями: нет Node.js API, нет файловой системы, только Web APIs.
// app/api/geo/route.ts — Edge Functionexport 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 Runtimeimport { 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 — понимаем пользователей
Заголовок раздела «📊 Vercel Analytics — понимаем пользователей»Vercel Analytics — это privacy-friendly аналитика. Никаких cookies, никаких GDPR-согласий!
npm install @vercel/analyticsimport { 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 — Core Web Vitals в реальном времени
Заголовок раздела «🏃 Speed Insights — Core Web Vitals в реальном времени»Speed Insights показывает реальные данные производительности от твоих пользователей:
npm install @vercel/speed-insightsimport { 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 Blob — хранение файлов»Vercel Blob — это хранилище файлов, интегрированное прямо в экосистему Vercel:
npm install @vercel/blobimport { 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 — Redis-совместимое хранилище
Заголовок раздела «🗝️ Vercel KV — Redis-совместимое хранилище»Vercel KV — это managed Redis-совместимое хранилище. Идеально для кэширования, сессий, rate limiting:
npm install @vercel/kvimport { 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');}🐘 Vercel Postgres — managed PostgreSQL
Заголовок раздела «🐘 Vercel Postgres — managed PostgreSQL»npm install @vercel/postgresimport { 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.comVercel покажет 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.comns2.vercel-dns.comVercel автоматически выпустит SSL-сертификат через Let’s Encrypt и настроит HTTPS. Без единой команды!
⏮️ Rollback деплоя
Заголовок раздела «⏮️ Rollback деплоя»Что-то пошло не так? Откатиться — одна кнопка:
Project → Deployments → (найди нужный деплой) → … → Promote to Production
Или через CLI:
# Просмотр всех деплоевvercel ls
# Откат к предыдущему деплоюvercel rollback [deployment-url]
# Пример:vercel rollback https://my-app-abc123.vercel.appVercel хранит все деплои — можно откатиться к любому моменту истории!
🛠️ Vercel CLI — управление из терминала
Заголовок раздела «🛠️ Vercel CLI — управление из терминала»# Установкаnpm install -g vercel
# Авторизацияvercel login
# Связывание проектаvercel link
# Локальная разработка с Vercel-функциямиvercel dev# Эмулирует Edge Functions, Serverless Functions, переменные окружения
# Деплой в previewvercel
# Деплой в productionvercel --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 lsvercel domains add my-domain.comvercel domains rm my-domain.com
# Алиасы деплоевvercel alias set [deployment-url] my-app.vercel.app🔄 Пример полного CI/CD пайплайна с Vercel
Заголовок раздела «🔄 Пример полного CI/CD пайплайна с Vercel»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💡 Полезные паттерны для Vercel
Заголовок раздела «💡 Полезные паттерны для Vercel»// Определение текущего 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'; // Локально}
// Генерация метаданных с правильным URLexport function generateMetadata(): Metadata { const baseUrl = getBaseUrl(); return { metadataBase: new URL(baseUrl), openGraph: { url: baseUrl, siteName: 'My Next.js App', }, };}// Revalidation по webhook от CMSimport { 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»| Фича | Vercel | Netlify | Railway | VPS |
|---|---|---|---|---|
| Next.js поддержка | ✅✅ Родная | ✅ Хорошая | ✅ Есть | ⚠️ Сам настраивай |
| Edge Functions | ✅ 100+ регионов | ✅ Есть | ❌ | ❌ |
| Preview Deployments | ✅ | ✅ | ✅ | ❌ |
| Analytics | ✅ Встроен | ⚠️ Плагин | ❌ | ❌ |
| Managed DB | ✅ Postgres/KV/Blob | ❌ | ✅ | ❌ |
| Бесплатный план | ✅ Щедрый | ✅ Есть | ⚠️ Лимиты | ❌ |
| Кастомные домены | ✅ | ✅ | ✅ | ✅ |
| Цена на старте | $0 | $0 | $5/мес | $5/мес |
Для большинства Next.js-проектов Vercel — это идеальное решение. Быстрый старт, масштабирование без боли, всё интегрировано.