89 lines
4.0 KiB
Markdown
89 lines
4.0 KiB
Markdown
|
|
---
|
|||
|
|
title: Серверный await
|
|||
|
|
description: Получение REST-данных на сервере до первого HTML.
|
|||
|
|
keywords: [rest, server components, await, nextjs, isr, ssr, notFound, redirect]
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Серверный await
|
|||
|
|
|
|||
|
|
Получение REST-данных на сервере до первого HTML.
|
|||
|
|
|
|||
|
|
Серверный `await` — базовая стратегия для данных, которые нужны до рендера страницы или серверного блока.
|
|||
|
|
|
|||
|
|
## Когда использовать
|
|||
|
|
|
|||
|
|
- Данные нужны для первого HTML.
|
|||
|
|
- Данные влияют на `metadata`.
|
|||
|
|
- По результату запроса нужно вызвать `notFound()` или `redirect()`.
|
|||
|
|
- Компонент серверный и данные не зависят от состояния браузера.
|
|||
|
|
|
|||
|
|
## Влияние на рендер
|
|||
|
|
|
|||
|
|
Серверный `await` сам по себе не означает SSR. В App Router страница может остаться static/ISR, если маршрут не использует dynamic API и запросы можно кешировать.
|
|||
|
|
|
|||
|
|
ISR — приоритет для общих данных. Если список или детальная страница могут обновляться по интервалу, сохраняйте кеширование и не добавляйте `no-store`, `revalidate: 0` или `force-dynamic` без требования.
|
|||
|
|
|
|||
|
|
SSR/dynamic rendering нужен, когда данные зависят от текущего request: cookie, headers, `searchParams`, preview-режим или персональные данные пользователя.
|
|||
|
|
|
|||
|
|
## Пример страницы списка
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
// src/app/(routes)/pets/page.tsx
|
|||
|
|
import { petStoreApi } from 'infrastructure/pet-store-api'
|
|||
|
|
import { PetsScreen } from 'screens/pets'
|
|||
|
|
|
|||
|
|
export default async function PetsPage() {
|
|||
|
|
const pets = await petStoreApi.pet.findPetsByStatus({
|
|||
|
|
status: 'available',
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
return <PetsScreen pets={pets} />
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`page.tsx` получает данные первого рендера и передаёт их ниже. UI страницы остаётся в `screens/`, а не пишется прямо в `app/`.
|
|||
|
|
|
|||
|
|
## Пример детальной страницы
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
// src/app/(routes)/pets/[id]/page.tsx
|
|||
|
|
import { notFound } from 'next/navigation'
|
|||
|
|
import { petStoreApi } from 'infrastructure/pet-store-api'
|
|||
|
|
import { PetDetailScreen } from 'screens/pet-detail'
|
|||
|
|
|
|||
|
|
type PetPageProps = {
|
|||
|
|
params: Promise<{ id: string }>
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default async function PetPage({ params }: PetPageProps) {
|
|||
|
|
const { id } = await params
|
|||
|
|
const pet = await petStoreApi.pet.getPetById(Number(id)).catch(() => null)
|
|||
|
|
|
|||
|
|
if (!pet) {
|
|||
|
|
notFound()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return <PetDetailScreen pet={pet} />
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Обработка 404 зависит от API-клиента и класса ошибок. В примере показана идея: решение о `notFound()` принимается на уровне маршрута, а не внутри REST-клиента.
|
|||
|
|
|
|||
|
|
## Что не делать
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
// Плохо — хуки нельзя вызывать в Server Component
|
|||
|
|
const { data } = useGetPetList('available')
|
|||
|
|
|
|||
|
|
// Плохо — прямой fetch в обход клиента
|
|||
|
|
const response = await fetch('https://petstore3.swagger.io/api/v3/pet/findByStatus')
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Если данные нужны на сервере, вызывайте метод REST-клиента напрямую.
|
|||
|
|
|
|||
|
|
## Когда выбрать другую стратегию
|
|||
|
|
|
|||
|
|
- Несколько независимых запросов — [Параллельные серверные запросы](./parallel-server-requests.md).
|
|||
|
|
- Часть UI можно грузить отдельно — [Передача промиса ниже](./pass-promise-down.md).
|
|||
|
|
- Данные нужны клиентскому хуку сразу после гидрации — [Начальные данные для клиентских хуков](./client-hooks-initial-data.md).
|