Типизация данных в getServerSideProps
getServerSideProps — это функция, которая выполняется на сервере при каждом запросе к странице. Она позволяет получать данные ещё до того, как страница рендерится, и передаёт их в компонент страницы через пропсы.
Пример базового использования:
import { GetServerSideProps } from 'next';
interface DataProps {
message: string;
}
const HomePage: React.FC<DataProps> = ({ message }) => {
return <h1>{message}</h1>;
};
export const getServerSideProps: GetServerSideProps = async () => {
// Получение данных на сервере
const data = { message: 'Hello from SSR!' };
return {
props: data, // Передаём данные в страницу
};
};
export default HomePage;
Ключевой момент: безопасность
Поскольку getServerSideProps выполняется только на сервере, код в этой функции никогда не будет загружен на клиент. Это даёт вам большую гибкость, например, для работы с приватными API или использования серверных секретов.
Почему важна типизация?
Когда вы работаете с данными, типизация помогает:
- Избежать ошибок при передаче данных в компонент.
- Улучшить автодополнение в редакторе.
- Сделать код читаемым и понятным для команды.
Пример без типизации:
export const getServerSideProps = async () => {
const data = { message: 'без типизации' };
return { props: data };
};
Попробуйте передать в компонент что-то нестандартное, например, объект или массив... и получите отлично типизированный хаос. Теперь давайте исправим это профессионально.
Как типизировать getServerSideProps?
Функция getServerSideProps из Next.js поддерживает типизацию через интерфейс GetServerSideProps. Давайте посмотрим, как это работает.
Пример с использованием интерфейсов:
import { GetServerSideProps } from 'next';
interface DataProps {
message: string;
}
const DataPage: React.FC<DataProps> = ({ message }) => {
return <div>{message}</div>;
};
export const getServerSideProps: GetServerSideProps<DataProps> = async () => {
const data: DataProps = { message: 'Типизация работает!' };
return {
props: data,
};
};
export default DataPage;
Здесь:
DataProps— интерфейс, описывающий структуру данных, которые возвращаетgetServerSideProps.GetServerSideProps<DataProps>— типизация самой функции, которая указывает, что она возвращает пропсы такой структуры.
Обработка сложных данных
Если ваши данные более сложные, например, содержат массивы или вложенные объекты, вы можете расширить интерфейс:
interface NestedData {
id: number;
name: string;
}
interface ComplexDataProps {
title: string;
items: NestedData[];
}
export const getServerSideProps: GetServerSideProps<ComplexDataProps> = async () => {
const data: ComplexDataProps = {
title: 'Список элементов',
items: [
{ id: 1, name: 'Элемент 1' },
{ id: 2, name: 'Элемент 2' },
],
};
return {
props: data,
};
};
Теперь вы уверены, что данные соответствуют структуре, и ошибки при передаче данных сведены к минимуму!
Обработка и передача данных на страницу
Когда данные приходят с API
Часто данные нужно запрашивать из внешнего API. Вот как это можно сделать:
export const getServerSideProps: GetServerSideProps = async () => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return {
props: { data }, // Передаём данные из API в компонент
};
};
Типизация ответа API
Когда вы знаете структуру ответа API, можно типизировать его для большей уверенности.
interface ApiResponse {
id: number;
name: string;
}
interface Props {
items: ApiResponse[];
}
export const getServerSideProps: GetServerSideProps<Props> = async () => {
const response = await fetch('https://api.example.com/items');
const data: ApiResponse[] = await response.json();
return {
props: { items: data },
};
};
const ItemPage: React.FC<Props> = ({ items }) => {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};
export default ItemPage;
Управление ошибками
Что делать, если API вернул ошибку? Не паникуем. Вместо этого:
- Обрабатываем ошибки в
getServerSideProps. - Показываем пользователю понятное сообщение.
export const getServerSideProps: GetServerSideProps = async () => {
try {
const response = await fetch('https://api.example.com/endpoint');
if (!response.ok) {
throw new Error('Ошибка API');
}
const data = await response.json();
return { props: { data } };
} catch (error) {
return { props: { error: 'Не удалось загрузить данные.' } };
}
};
const ErrorPage: React.FC<{ data?: any; error?: string }> = ({ data, error }) => {
if (error) return <div>{error}</div>;
return <div>{JSON.stringify(data)}</div>;
};
export default ErrorPage;
Теперь, даже если API упадёт, ваш пользователь не увидит "белую страницу смерти".
Паттерны и оптимизация работы с данными
Работу с данными можно разделить на три ключевых этапа:
- Запрос данных — выбор, где и как запросить данные (API, база данных).
- Обработка данных — фильтрация, трансформация или выборка нужной информации.
- Передача данных — передача данных в компонент с минимальными изменениями.
Использование служб для запроса данных
Лучше выделить запрос данных в отдельную функцию для повторного использования:
// services/api.ts
export async function fetchItems(): Promise<ApiResponse[]> {
const response = await fetch('https://api.example.com/items');
if (!response.ok) throw new Error('Ошибка загрузки данных');
return response.json();
}
// pages/items.tsx
import { fetchItems } from '../services/api';
export const getServerSideProps: GetServerSideProps = async () => {
try {
const items = await fetchItems();
return { props: { items } };
} catch {
return { props: { items: [] } };
}
};
Управление ошибками в getServerSideProps
Обработка ошибок важна особенно в условиях работы с сетевыми запросами. Если API упал или серверная логика вызвала исключение, у вас должен быть план "Б".
- Возвращайте заранее определённый интерфейс ошибок:
interface ErrorProps {
error: true;
message: string;
}
interface SuccessProps {
error: false;
data: ApiResponse[];
}
type Props = ErrorProps | SuccessProps;
export const getServerSideProps: GetServerSideProps<Props> = async () => {
try {
const data = await fetchItems();
return { props: { error: false, data } };
} catch {
return { props: { error: true, message: 'Сбой загрузки данных' } };
}
};
const ErrorHandlingPage: React.FC<Props> = (props) => {
if (props.error) return <div>{props.message}</div>;
return (
<ul>
{props.data.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};
- Показывайте пользователю полезную информацию, а не пустую страницу.
Теперь вы вооружены полной типизацией и готовы творить безопасные, надёжные SSR-приложения!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ