Введение
Next.js — один из самых популярных React фреймворков для создания full-stack приложений с поддержкой SSR (Server-Side Rendering) и SSG (Static Site Generation). Но что делать, если backend еще не готов, а нужно начать разработку с SSR и SSG?
В этом туториале вы научитесь интегрировать Next.js с Mock API для работы с SSR и SSG. Мы рассмотрим все способы работы с данными в Next.js: getServerSideProps, getStaticProps, API Routes и клиентские запросы с SWR и React Query.
✅ Что вы получите в конце:
- ✅ Полноценное Next.js приложение с Mock API
- ✅ SSR с getServerSideProps и Mock API
- ✅ SSG с getStaticProps и Mock API
- ✅ API Routes в Next.js
- ✅ SWR и React Query интеграция
- ✅ Универсальный API клиент для сервера и клиента
- ✅ Middleware для защиты API Routes
- ✅ TypeScript поддержка
- ✅ Deploy на Vercel
💡 Для кого этот туториал:
- Next.js разработчики: Научитесь работать с Mock API в SSR/SSG
- Full-stack команды: Начните фронтенд разработку без ожидания backend
- Опытные разработчики: Ускорьте прототипирование с Mock API
- Студенты: Изучите Next.js и SSR/SSG на практике
📋 План действий (15 минут)
- Что такое Next.js SSR и SSG (1 мин)
- Шаг 1: Setup проекта (2 мин)
- Шаг 2: Создание Mock API (3 мин)
- Шаг 3: Универсальный API клиент (2 мин)
- Шаг 4: getServerSideProps + Mock API (2 мин)
- Шаг 5: getStaticProps + Mock API (2 мин)
- Шаг 6: SWR интеграция (2 мин)
- Шаг 7: API Routes (1 мин)
Что такое Next.js SSR и SSG? 🚀
Next.js поддерживает три способа рендеринга:
| Тип рендеринга | Описание | Когда использовать |
|---|---|---|
| SSR (Server-Side Rendering) | Страница генерируется на сервере при каждом запросе | Динамический контент, персональные данные |
| SSG (Static Site Generation) | Страница генерируется на этапе сборки | Статический контент, блог, документация |
| CSR (Client-Side Rendering) | Страница рендерится в браузере | Интерактивные части, дашборды |
Data Fetching методы в Next.js:
- getServerSideProps: Выполняется на сервере при каждом запросе (SSR)
- getStaticProps: Выполняется на этапе сборки (SSG)
- getStaticPaths: Определяет динамические пути для SSG
- API Routes: Создание API endpoints прямо в Next.js
- SWR / React Query: Клиентские запросы с кэшированием
Шаг 1: Setup проекта (2 минуты)
1 Создайте Next.js приложение
# Создаем Next.js проект с TypeScript
npx create-next-app@latest my-app --typescript --tailwind --app
# Или с Pages Router
npx create-next-app@latest my-app --typescript --tailwind
# Переходим в директорию
cd my-app
# Устанавливаем дополнительные зависимости
npm install swr axios
2 Структура проекта
my-app/
├── pages/ # Pages Router
│ ├── api/ # API Routes
│ │ └── todos/
│ │ └── [id].ts
│ ├── _app.tsx # Главный компонент
│ ├── index.tsx # Главная страница
│ └── todos/
│ ├── index.tsx # Список todos
│ └── [id].tsx # Детали todo
├── lib/
│ └── api.ts # API клиент
├── types/
│ └── todo.ts # TypeScript типы
├── .env.local # Переменные окружения
└── next.config.js
# Или с App Router
my-app/
├── app/
│ ├── api/ # API Routes
│ ├── page.tsx # Главная страница
│ └── todos/
│ └── page.tsx
├── lib/
│ └── api.ts
└── types/
└── todo.ts
Шаг 2: Создание Mock API (3 минуты)
1 Зарегистрируйтесь на LightBox API
Перейдите на lightboxapi.ru и создайте бесплатный аккаунт.
2 Создайте workspace
В dashboard создайте новый workspace с именем nextjs-todos.
3 Настройте endpoints
Endpoint 1: GET /todos
{
"todos": [
{
"id": "1",
"title": "Изучить Next.js SSR",
"completed": false,
"createdAt": "2025-10-28T10:00:00Z"
},
{
"id": "2",
"title": "Интегрировать Mock API",
"completed": true,
"createdAt": "2025-10-28T09:00:00Z"
}
]
}
Endpoint 2: GET /todos/:id
{
"id": "{params.id}",
"title": "Todo title",
"completed": false,
"createdAt": "2025-10-28T10:00:00Z"
}
Endpoint 3: POST /todos
{
"id": "{randomUUID}",
"title": "{body.title}",
"completed": false,
"createdAt": "{$timestamp}"
}
Скопируйте URL вашего Mock API: https://lightboxapi.ru/your-workspace
Шаг 3: Универсальный API клиент (2 минуты)
В Next.js нужно создавать API клиент, который работает как на сервере (в getServerSideProps/getStaticProps), так и на клиенте. Важно определить базовый URL правильно.
1 Создайте TypeScript типы
Файл: types/todo.ts
export interface Todo {
id: string;
title: string;
completed: boolean;
createdAt: string;
}
export interface CreateTodoDto {
title: string;
}
2 Создайте универсальный API клиент
Файл: lib/api.ts
import type { Todo, CreateTodoDto } from '../types/todo';
// Определяем базовый URL
const getBaseUrl = () => {
// На сервере используем полный URL
if (typeof window === 'undefined') {
return process.env.NEXT_PUBLIC_API_URL || 'https://lightboxapi.ru/your-workspace';
}
// На клиенте используем относительный или полный URL
return process.env.NEXT_PUBLIC_API_URL || 'https://lightboxapi.ru/your-workspace';
};
const API_BASE_URL = getBaseUrl();
// API клиент
export const api = {
// Получить все todos
async getTodos(): Promise {
const response = await fetch(`${API_BASE_URL}/todos`);
if (!response.ok) {
throw new Error('Failed to fetch todos');
}
const data = await response.json();
return data.todos || data;
},
// Получить todo по ID
async getTodo(id: string): Promise {
const response = await fetch(`${API_BASE_URL}/todos/${id}`);
if (!response.ok) {
throw new Error('Failed to fetch todo');
}
return response.json();
},
// Создать todo
async createTodo(data: CreateTodoDto): Promise {
const response = await fetch(`${API_BASE_URL}/todos`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error('Failed to create todo');
}
return response.json();
},
// Обновить todo
async updateTodo(id: string, data: Partial): Promise {
const response = await fetch(`${API_BASE_URL}/todos/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error('Failed to update todo');
}
return response.json();
},
// Удалить todo
async deleteTodo(id: string): Promise {
const response = await fetch(`${API_BASE_URL}/todos/${id}`, {
method: 'DELETE',
});
if (!response.ok) {
throw new Error('Failed to delete todo');
}
},
};
3 Настройте переменные окружения
Файл: .env.local
NEXT_PUBLIC_API_URL=https://lightboxapi.ru/your-workspace
⚠️ Важно:
В Next.js переменные окружения для клиента должны начинаться с NEXT_PUBLIC_.
Иначе они не будут доступны на клиенте.
Шаг 4: getServerSideProps + Mock API (2 минуты)
getServerSideProps выполняется на сервере при каждом запросе. Идеально для динамического контента, который зависит от пользователя или времени.
1 Создайте страницу с SSR
Файл: pages/todos/index.tsx
import { GetServerSideProps } from 'next';
import type { Todo } from '../../types/todo';
import { api } from '../../lib/api';
interface TodosPageProps {
todos: Todo[];
}
export default function TodosPage({ todos }: TodosPageProps) {
return (
Todos
{todos.map((todo) => (
-
{todo.title} - {todo.completed ? '✅' : '⭕'}
))}
);
}
// SSR: Выполняется на сервере при каждом запросе
export const getServerSideProps: GetServerSideProps = async () => {
try {
const todos = await api.getTodos();
return {
props: {
todos,
},
};
} catch (error) {
console.error('Error fetching todos:', error);
return {
props: {
todos: [],
},
};
}
};
2 Динамическая страница с SSR
Файл: pages/todos/[id].tsx
import { GetServerSideProps } from 'next';
import type { Todo } from '../../types/todo';
import { api } from '../../lib/api';
interface TodoPageProps {
todo: Todo;
}
export default function TodoPage({ todo }: TodoPageProps) {
return (
{todo.title}
Status: {todo.completed ? 'Completed' : 'Pending'}
Created: {new Date(todo.createdAt).toLocaleString()}
);
}
export const getServerSideProps: GetServerSideProps = async (context) => {
const { id } = context.params!;
try {
const todo = await api.getTodo(id as string);
return {
props: {
todo,
},
};
} catch (error) {
return {
notFound: true, // Вернуть 404
};
}
};
Шаг 5: getStaticProps + Mock API (2 минуты)
getStaticProps выполняется на этапе сборки (build time). Страницы генерируются статически и могут быть закэшированы. Идеально для блогов, документации и контента, который редко меняется.
1 SSG с getStaticProps
Файл: pages/todos/index.tsx
import { GetStaticProps } from 'next';
import type { Todo } from '../../types/todo';
import { api } from '../../lib/api';
interface TodosPageProps {
todos: Todo[];
}
export default function TodosPage({ todos }: TodosPageProps) {
return (
Todos (Static Generated)
{todos.map((todo) => (
- {todo.title}
))}
);
}
// SSG: Выполняется на этапе сборки
export const getStaticProps: GetStaticProps = async () => {
try {
const todos = await api.getTodos();
return {
props: {
todos,
},
// Revalidate каждые 60 секунд (Incremental Static Regeneration)
revalidate: 60,
};
} catch (error) {
return {
props: {
todos: [],
},
};
}
};
2 SSG с динамическими путями
Файл: pages/todos/[id].tsx
import { GetStaticPaths, GetStaticProps } from 'next';
import type { Todo } from '../../types/todo';
import { api } from '../../lib/api';
interface TodoPageProps {
todo: Todo;
}
export default function TodoPage({ todo }: TodoPageProps) {
return (
{todo.title}
Status: {todo.completed ? 'Completed' : 'Pending'}
);
}
// Определяем какие пути генерировать
export const getStaticPaths: GetStaticPaths = async () => {
try {
const todos = await api.getTodos();
const paths = todos.map((todo) => ({
params: { id: todo.id },
}));
return {
paths,
fallback: 'blocking', // или 'true' для ISR
};
} catch (error) {
return {
paths: [],
fallback: 'blocking',
};
}
};
// Генерируем страницу для каждого пути
export const getStaticProps: GetStaticProps = async (context) => {
const { id } = context.params!;
try {
const todo = await api.getTodo(id as string);
return {
props: {
todo,
},
revalidate: 60, // ISR: обновлять каждые 60 секунд
};
} catch (error) {
return {
notFound: true,
};
}
};
💡 Fallback варианты:
- false: Только предгенерированные пути, остальные 404
- true: ISR - страницы генерируются по требованию
- 'blocking': ISR - ждёт генерации перед показом страницы
Шаг 6: SWR интеграция (2 минуты)
SWR (stale-while-revalidate) — библиотека от создателей Next.js для клиентских запросов. Она обеспечивает кэширование, ревалидацию и автоматическое обновление данных.
1 Настройте SWR Provider
Файл: pages/_app.tsx
import type { AppProps } from 'next/app';
import { SWRConfig } from 'swr';
function MyApp({ Component, pageProps }: AppProps) {
return (
fetch(url).then((res) => res.json()),
revalidateOnFocus: true,
revalidateOnReconnect: true,
}
>
);
}
export default MyApp;
2 Используйте SWR в компонентах
Файл: pages/todos/index.tsx
import useSWR from 'swr';
import { api } from '../../lib/api';
import type { Todo } from '../../types/todo';
export default function TodosPage() {
const { data, error, isLoading, mutate } = useSWR(
'/api/todos',
() => api.getTodos()
);
if (isLoading) return Loading...;
if (error) return Error: {error.message};
const handleCreate = async () => {
const newTodo = await api.createTodo({ title: 'New Todo' });
// Обновить кэш SWR
mutate();
};
return (
Todos (Client-Side)
{data?.map((todo) => (
- {todo.title}
))}
);
}
3 Комбинация SSR + SWR
Лучший подход: предзагрузить данные через SSR, затем использовать SWR для обновлений.
import { GetServerSideProps } from 'next';
import useSWR from 'swr';
import { api } from '../../lib/api';
import type { Todo } from '../../types/todo';
interface TodosPageProps {
initialTodos: Todo[];
}
export default function TodosPage({ initialTodos }: TodosPageProps) {
// Используем initialTodos как fallback для SWR
const { data: todos = initialTodos, mutate } = useSWR(
'/api/todos',
() => api.getTodos(),
{
fallbackData: initialTodos, // Начальные данные из SSR
}
);
return (
Todos (SSR + Client Updates)
{todos.map((todo) => (
- {todo.title}
))}
);
}
export const getServerSideProps: GetServerSideProps = async () => {
const initialTodos = await api.getTodos();
return { props: { initialTodos } };
};
Шаг 7: API Routes в Next.js (1 минута)
API Routes позволяют создавать API endpoints прямо в Next.js приложении. Это удобно для создания прокси к внешнему Mock API или добавления серверной логики.
1 Создайте API Route
Файл: pages/api/todos/index.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { api } from '../../../lib/api';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === 'GET') {
try {
const todos = await api.getTodos();
res.status(200).json(todos);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch todos' });
}
} else if (req.method === 'POST') {
try {
const todo = await api.createTodo(req.body);
res.status(201).json(todo);
} catch (error) {
res.status(500).json({ error: 'Failed to create todo' });
}
} else {
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
2 Динамический API Route
Файл: pages/api/todos/[id].ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { api } from '../../../lib/api';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { id } = req.query;
if (req.method === 'GET') {
try {
const todo = await api.getTodo(id as string);
res.status(200).json(todo);
} catch (error) {
res.status(404).json({ error: 'Todo not found' });
}
} else if (req.method === 'PUT') {
try {
const todo = await api.updateTodo(id as string, req.body);
res.status(200).json(todo);
} catch (error) {
res.status(500).json({ error: 'Failed to update todo' });
}
} else if (req.method === 'DELETE') {
try {
await api.deleteTodo(id as string);
res.status(204).end();
} catch (error) {
res.status(500).json({ error: 'Failed to delete todo' });
}
} else {
res.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
App Router (Next.js 13+) 🆕
В Next.js 13+ появился новый App Router. Работа с данными немного отличается.
1 Server Components с App Router
Файл: app/todos/page.tsx
import { api } from '@/lib/api';
import type { Todo } from '@/types/todo';
// Server Component по умолчанию
export default async function TodosPage() {
// Прямой fetch на сервере
const todos = await api.getTodos();
return (
Todos (Server Component)
{todos.map((todo) => (
- {todo.title}
))}
);
}
2 Client Components с SWR
Файл: app/todos/page.tsx
'use client'; // Указываем, что это Client Component
import useSWR from 'swr';
import { api } from '@/lib/api';
import type { Todo } from '@/types/todo';
export default function TodosPage() {
const { data, error, isLoading } = useSWR(
'todos',
() => api.getTodos()
);
if (isLoading) return Loading...;
if (error) return Error: {error.message};
return (
Todos (Client Component)
{data?.map((todo) => (
- {todo.title}
))}
);
}
Middleware для защиты API Routes 🛡️
Middleware в Next.js позволяет перехватывать запросы и добавлять логику, например, проверку аутентификации или rate limiting.
// Файл: middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Защита API Routes
if (request.nextUrl.pathname.startsWith('/api/')) {
const apiKey = request.headers.get('X-API-Key');
if (!apiKey || apiKey !== process.env.API_KEY) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
);
}
}
return NextResponse.next();
}
export const config = {
matcher: '/api/:path*',
};
Best Practices для Next.js + Mock API 🌟
✅ Рекомендации:
- Используйте SSR для динамического контента: getServerSideProps для персональных данных
- Используйте SSG для статического контента: getStaticProps для блогов, документации
- Комбинируйте SSR + SWR: Предзагрузите данные через SSR, обновляйте через SWR
- Создавайте универсальный API клиент: Работает на сервере и клиенте
- Используйте API Routes как прокси: Скрывайте внешний API от клиента
- Настройте правильные переменные окружения: NEXT_PUBLIC_ для клиента
- Используйте TypeScript: Типизация для безопасности
- Добавьте обработку ошибок: Graceful fallbacks при недоступности API
Deploy на Vercel 🚀
1 Подготовьте проект
# Соберите проект
npm run build
# Проверьте, что сборка прошла успешно
npm run start
2 Deploy на Vercel
- Подключите GitHub репозиторий к Vercel
- Добавьте переменную окружения
NEXT_PUBLIC_API_URLв настройках проекта - Vercel автоматически определит Next.js и выполнит сборку
- Ваше приложение будет доступно по URL от Vercel
Заключение
Вы научились интегрировать Next.js с Mock API для всех сценариев рендеринга: SSR, SSG и клиентские запросы. Это позволяет начать разработку сразу, без ожидания backend.
💡 Что мы изучили:
- ✅ Создание универсального API клиента для сервера и клиента
- ✅ Использование Mock API в getServerSideProps (SSR)
- ✅ Использование Mock API в getStaticProps (SSG)
- ✅ Интеграция SWR для клиентских запросов
- ✅ Создание API Routes в Next.js
- ✅ Работа с App Router (Next.js 13+)
- ✅ Middleware для защиты API Routes
- ✅ Deploy на Vercel
Создайте Mock API за 2 минуты
Начните разработку Next.js приложения с SSR и SSG прямо сейчас с LightBox API. Не ждите backend — создайте Mock API за 2 минуты и начните разработку.
Попробовать бесплатно →