Структура API эндпоинтов

Fresh — это современный фреймворк для разработки веб-приложений на JavaScript и TypeScript, ориентированный на высокую производительность и минимизацию клиентской нагрузки. Основная идея Fresh заключается в использовании Edge Rendering и генерации HTML на сервере, без необходимости большого объема клиентского JavaScript.


Архитектура Fresh

Fresh строится вокруг следующих ключевых компонентов:

  • Роуты (Routes) — определяют URL-эндпоинты приложения. Каждый файл в папке routes автоматически становится маршрутом.
  • Island Components — изолированные интерактивные компоненты, которые загружаются на клиент только при необходимости, минимизируя объём JavaScript.
  • Middleware — функции, выполняющиеся перед обработкой запроса, например для аутентификации или логирования.
  • Handlers (Request Handlers) — функции, которые управляют ответами на HTTP-запросы.

Структура API-эндпоинтов

API в Fresh организуется через файловую структуру в директории routes. Каждый файл может экспортировать методы HTTP, соответствующие действиям с ресурсами.

Пример структуры:

routes/
├── api/
│   ├── users.ts
│   ├── posts/
│   │   └── [id].ts
  • users.ts отвечает за коллекцию пользователей (GET, POST и т.д.)
  • [id].ts — динамический маршрут для конкретного пользователя по идентификатору

Обработка HTTP-запросов

В файле API можно экспортировать функции для разных методов HTTP:

import { Handlers } from "$fresh/server.ts";

export const handler: Handlers = {
  GET(req, ctx) {
    return new Response(JSON.stringify({ message: "Список пользователей" }), {
      headers: { "Content-Type": "application/json" },
    });
  },
  POST(req, ctx) {
    return new Response(JSON.stringify({ message: "Пользователь создан" }), {
      headers: { "Content-Type": "application/json" },
    });
  },
};

Ключевые моменты:

  • Handlers — интерфейс для строгой типизации методов.
  • req содержит объект запроса, включая тело, заголовки и параметры.
  • ctx предоставляет контекст маршрута, включая динамические параметры и состояние.

Динамические маршруты

Динамические маршруты создаются через квадратные скобки [param]. Параметр автоматически доступен через ctx.params:

export const handler: Handlers = {
  GET(req, ctx) {
    const userId = ctx.params.id;
    return new Response(JSON.stringify({ id: userId, name: "Иван" }));
  },
};

Особенности:

  • Параметры всегда строки, требуется явное преобразование типов при необходимости.
  • Динамические маршруты могут быть вложенными (posts/[postId]/comments/[commentId].ts).

Работа с телом запроса и JSON

Для чтения тела запроса используется метод req.json():

export const handler: Handlers = {
  POST: async (req) => {
    const data = await req.json();
    return new Response(JSON.stringify({ received: data }), {
      headers: { "Content-Type": "application/json" },
    });
  },
};

Ключевые моменты:

  • Метод json() возвращает объект, соответствующий JSON-телу запроса.
  • Необходимо обрабатывать исключения, если тело невалидное.

Заголовки и статус ответа

Можно управлять статусом и заголовками ответа:

return new Response(JSON.stringify({ error: "Не найдено" }), {
  status: 404,
  headers: { "Content-Type": "application/json" },
});
  • status определяет код HTTP-ответа.
  • headers позволяет задавать тип контента, кэширование и другие параметры.

Поддержка Middleware

Middleware выполняются до основных обработчиков и позволяют:

  • Проверять аутентификацию
  • Логировать запросы
  • Перехватывать ошибки

Пример Middleware для проверки токена:

export const handler: Handlers = {
  async GET(req, ctx) {
    const token = req.headers.get("Authorization");
    if (!token) {
      return new Response("Unauthorized", { status: 401 });
    }
    return new Response("Доступ разрешен");
  },
};

Статические данные и SSR

Fresh позволяет возвращать статический контент или генерировать страницы на сервере, используя Server-Side Rendering:

import { PageProps } from "$fresh/server.ts";

export default function UsersPage({ data }: PageProps) {
  return (
    <ul>
      {data.users.map((user: any) => (
        <li>{user.name}</li>
      ))}
    </ul>
  );
}
  • data передается из обработчика маршрута.
  • Компоненты рендерятся на сервере и отправляются в браузер готовым HTML.

Интеграция с базой данных

API-эндпоинты легко подключаются к базе данных. Пример с PostgreSQL:

import { client } from "../db.ts";

export const handler: Handlers = {
  async GET() {
    const result = await client.queryArray("SELECT * FROM users");
    return new Response(JSON.stringify(result.rows), {
      headers: { "Content-Type": "application/json" },
    });
  },
};

Особенности:

  • Асинхронные функции поддерживаются нативно.
  • Важно обрабатывать ошибки базы данных для предотвращения утечек.

Практические советы

  • Каждый API-эндпоинт должен быть одноответственным, обрабатывая только свою задачу.
  • Динамические маршруты упрощают REST-структуру.
  • Использование Island Components позволяет минимизировать клиентский JavaScript даже для интерактивных страниц.
  • Всегда задавать Content-Type в JSON-ответах для корректной обработки клиентом.
  • Структурирование проекта по папкам и подмаршрутам делает код более поддерживаемым.

Fresh сочетает простоту серверного рендеринга с современными практиками разработки, позволяя строить быстрые и безопасные веб-приложения с чистой архитектурой API.