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

16. Загрузка данных

Одна из суперсил Astro — загрузка данных прямо в фронтматере компонента с помощью top-level await. Никаких хуков, никаких useEffect — просто await fetch() на этапе рендеринга.

Думай о фронтматере Astro как о серверном скрипте: он выполняется один раз (при сборке или запросе), имеет доступ к Node.js API и может делать любые асинхронные операции.


---
// Это выполняется на сервере / при сборке
const response = await fetch('https://api.example.com/posts');
const posts = await response.json();
---
<ul>
{posts.map(post => <li>{post.title}</li>)}
</ul>

---
// Выполняется ОДИН РАЗ при npm run build
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
// Результат встраивается в HTML навсегда
---

Плюсы: молниеносная загрузка, CDN, нет нагрузки на сервер.
Минусы: данные устаревают до следующего билда.

---
// astro.config.mjs: output: 'server'
// Выполняется при КАЖДОМ запросе
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
// Данные всегда свежие
---

Плюсы: свежие данные, персонализация, доступ к cookies/headers.
Минусы: задержка на каждый запрос.


---
// ❌ Медленно — запросы идут последовательно
const posts = await fetch('/api/posts').then(r => r.json());
const user = await fetch('/api/user').then(r => r.json());
const config = await fetch('/api/config').then(r => r.json());
// ✅ Быстро — все запросы параллельно
const [posts, user, config] = await Promise.all([
fetch('/api/posts').then(r => r.json()),
fetch('/api/user').then(r => r.json()),
fetch('/api/config').then(r => r.json()),
]);
---

---
let posts = [];
let error = null;
try {
const res = await fetch('https://api.example.com/posts');
if (!res.ok) throw new Error(\`HTTP \${res.status}\`);
posts = await res.json();
} catch (e) {
error = e.message;
console.error('Ошибка загрузки:', e);
}
---
{error ? (
<p class="error">Ошибка: {error}</p>
) : (
<ul>{posts.map(p => <li>{p.title}</li>)}</ul>
)}

---
// .env файл:
// PUBLIC_API_URL=https://api.example.com
// SECRET_API_KEY=super-secret-key
const apiUrl = import.meta.env.PUBLIC_API_URL;
const apiKey = import.meta.env.SECRET_API_KEY; // Только в фронтматере!
const data = await fetch(\`\${apiUrl}/posts\`, {
headers: { Authorization: \`Bearer \${apiKey}\` }
}).then(r => r.json());
---

PUBLIC_* — доступны в браузере, остальные — только на сервере.


---
// CMS (Contentful, Sanity, Strapi)
const { items } = await fetch(\`https://cdn.contentful.com/spaces/\${spaceId}/entries\`)
.then(r => r.json());
// База данных (через Astro DB или Prisma)
import { db, Post } from 'astro:db';
const posts = await db.select().from(Post);
// Файловая система (через Content Collections)
import { getCollection } from 'astro:content';
const posts = await getCollection('blog');
// GraphQL
const { data } = await fetch('https://api.example.com/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: '{ posts { title } }' }),
}).then(r => r.json());
---