Astro: API Route (endpoint) с динамическим путём
Создание REST endpoint в Astro: APIRoute, динамический сегмент [slug], GET/POST и опциональная авторизация по заголовку.
Как использовать
- Создайте файл в src/pages/api/ с именем метода (GET, POST и т.д.) и при необходимости динамическим сегментом [slug].
- Для работы по запросу укажите output: server или hybrid в astro.config и export const prerender = false в файле endpoint.
- Проверьте запросом: curl http://localhost:4321/api/.../view или через браузер.
Когда в Astro-проекте нужен REST API (счётчики просмотров, webhook, админка по токену), эндпоинты делают в src/pages/ в виде файлов с экспортами GET, POST и т.д. Проблема возникает при переходе с чисто статического сайта: в режиме output: "static" такие маршруты вызываются только при сборке, а динамические пути без getStaticPaths не генерируются. Симптомы: после деплоя запрос к /api/blog/post-1/view отдаёт 404 или endpoint срабатывает только при build. Ниже — как сделать API Route с динамическим сегментом, несколькими методами и проверкой авторизации, плюс команда проверки через curl.
Решение
API Routes в Astro — это серверные endpoints в src/pages/. В режиме output: "static" эндпоинты вызываются при сборке; для работы по запросу нужен режим server или hybrid и для конкретного файла — export const prerender = false.
Динамический endpoint (Endpoints | Astro Docs):
// src/pages/api/blog/[slug]/view.ts
import type { APIRoute } from "astro";
export const prerender = false; // on-demand в static/hybrid
export const GET: APIRoute = async ({ params }) => {
const { slug } = params;
// запрос к БД: const views = await getViewsCount(slug);
const views = 0;
return new Response(
JSON.stringify({ slug, views }),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
);
};
export const POST: APIRoute = async ({ params, request }) => {
const { slug } = params;
// await incrementViews(slug);
return new Response(
JSON.stringify({ success: true }),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
);
};
Несколько HTTP-методов и ALL (HTTP methods):
// src/pages/api/products/[id].ts
import type { APIRoute } from "astro";
export const GET: APIRoute = async ({ params }) => {
return new Response(
JSON.stringify({ id: params.id, name: "Product" }),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
};
export const PUT: APIRoute = async ({ params, request }) => {
const body = await request.json();
return new Response(
JSON.stringify({ success: true }),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
};
export const DELETE: APIRoute = async ({ params }) => {
return new Response(
JSON.stringify({ success: true }),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
};
export const ALL: APIRoute = async ({ request }) => {
return new Response(
JSON.stringify({ error: `Method ${request.method} not supported` }),
{ status: 405 }
);
};
Проверка авторизации в API Route:
// src/pages/api/admin/posts.ts
import type { APIRoute } from "astro";
const ADMIN_TOKEN = import.meta.env.ADMIN_TOKEN;
export const GET: APIRoute = async ({ request }) => {
const token = request.headers.get("Authorization")?.replace("Bearer ", "");
if (token !== ADMIN_TOKEN) {
return new Response(
JSON.stringify({ error: "Unauthorized" }),
{ status: 401, headers: { "Content-Type": "application/json" } }
);
}
const posts: unknown[] = [];
return new Response(JSON.stringify(posts), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
- Имя файла с расширением
.tsили.jsзадаёт путь:api/blog/[slug]/view.ts→/api/blog/:slug/view. - Экспортируйте функции с именами методов:
GET,POST,PUT,DELETE,ALL. - Контекст handler:
{ params, request, redirect }и др. (API Reference).
Проверка
- Локальный запрос к динамическому endpoint (сервер должен быть в режиме server/hybrid):
pnpm dev
curl -s "http://localhost:4321/api/blog/hello-world/view"
Ожидаемый вывод — JSON, например {"slug":"hello-world","views":0}. Если 404 — проверьте, что в astro.config.mjs указано output: "server" или "hybrid" и в файле endpoint есть export const prerender = false.
- Диагностика метода и заголовков:
curl -s -o /dev/null -w "%{http_code}" "http://localhost:4321/api/blog/test/view"
curl -s -X POST "http://localhost:4321/api/blog/test/view"
Первый запрос — GET, ожидается 200. Второй — POST, должен вернуть JSON с success: true (или вашу логику).
- Проверка авторизации (для endpoint с ADMIN_TOKEN):
curl -s -w "\n%{http_code}" "http://localhost:4321/api/admin/posts"
# без заголовка — 401
curl -s -w "\n%{http_code}" -H "Authorization: Bearer ваш_токен" "http://localhost:4321/api/admin/posts"
# с верным токеном — 200 и тело с данными
Типичные ошибки
- Endpoint отдаёт 404 в dev или после деплоя — в статическом режиме динамические пути без
getStaticPathsне создаются. Включитеoutput: "server"или"hybrid"в конфиге и добавьте в файл endpointexport const prerender = false. - params пустой или undefined — убедитесь, что папка названа именно
[slug](квадратные скобки), а не:slugили{slug}. Имена сегментов берутся из имён папок/файлов. - На продакшене 404 при обращении к /api/… — на хостинге должен быть выбран adapter для Node/Netlify/Vercel и сборка с
output: "server"или"hybrid".
Где применять
- Разработка:
pnpm devприoutput: "server"или"hybrid". - Продакшен: деплой с адаптером (Node, Netlify, Vercel). Endpoint-файлы с
prerender: falseобрабатываются на сервере при каждом запросе.
Связанные сниппеты: Astro Actions: форма с валидацией Zod, Адаптеры для деплоя (Node, Netlify, Vercel), Статический вывод по умолчанию.
⚠️ В статическом режиме для динамических путей без getStaticPaths endpoint не будет сгенерирован. Используйте output: "server" или "hybrid" и при необходимости export const prerender = false в файле endpoint.