Интеграция с REST API

Fresh — это современный фреймворк для создания веб-приложений на базе Deno, который отличается высокой производительностью и минимальной зависимостью от внешних библиотек. В отличие от традиционных SPA, Fresh использует регенерацию страниц на сервере без необходимости бандлить код для клиента, что снижает нагрузку на сеть и ускоряет время первой загрузки. Центральное понятие Fresh — это ресурсы и острова (islands), которые позволяют интерактивным компонентам существовать внутри статической разметки без потери производительности.

Работа с REST API

Интеграция с REST API в Fresh строится вокруг стандартных возможностей Deno и современного JavaScript. В основе лежит fetch, который работает нативно в Deno и позволяет выполнять HTTP-запросы на сервер. Основные сценарии взаимодействия включают:

  • Получение данных с сервера (GET)
  • Отправка данных на сервер (POST, PUT, PATCH)
  • Удаление ресурсов (DELETE)

Пример запроса GET:

const response = await fetch("https://api.example.com/users");
if (!response.ok) {
    throw new Error(`Ошибка запроса: ${response.status}`);
}
const data = await response.json();
console.log(data);

Использование серверных обработчиков

Fresh поддерживает серверные обработчики (routes), которые позволяют обрабатывать запросы на сервере перед рендерингом страницы. Это особенно удобно для работы с REST API:

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

export const handler: Handlers = {
  async GET(req) {
    const response = await fetch("https://api.example.com/users");
    const users = await response.json();
    return new Response(JSON.stringify(users), {
      headers: { "Content-Type": "application/json" },
    });
  },
};

Использование серверных обработчиков позволяет централизовать взаимодействие с API, снижая количество сетевых запросов на клиенте и обеспечивая безопасное хранение ключей и токенов.

Работа с интерактивными компонентами (Islands)

Fresh использует концепцию островов, когда интерактивные компоненты загружаются на клиенте по мере необходимости. Для интеграции с REST API это особенно полезно:

// islands/UserList.tsx
import { useState, useEffect } from "preact/hooks";

export default function UserList() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch("/api/users")
      .then(res => res.json())
      .then(data => setUsers(data))
      .catch(err => console.error(err));
  }, []);

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

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

  • Компонент загружается на клиенте только при необходимости.
  • Данные извлекаются через локальный серверный API, что скрывает прямые вызовы внешних ресурсов.
  • Обновление состояния через useState обеспечивает реактивность без полноценного SPA-фреймворка.

Работа с формами и методами POST/PUT/DELETE

Fresh позволяет обрабатывать формы с отправкой данных через серверные обработчики. Пример добавления нового пользователя:

// routes/api/users.ts
export const handler: Handlers = {
  async POST(req) {
    const user = await req.json();
    const response = await fetch("https://api.example.com/users", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(user),
    });
    const createdUser = await response.json();
    return new Response(JSON.stringify(createdUser), {
      headers: { "Content-Type": "application/json" },
    });
  },
};

На клиенте форма может быть обработана следующим образом:

async function addUser(event) {
  event.preventDefault();
  const formData = new FormData(event.target);
  const user = {
    name: formData.get("name"),
    email: formData.get("email"),
  };
  const response = await fetch("/api/users", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(user),
  });
  const result = await response.json();
  console.log(result);
}

Обработка ошибок и таймаутов

При работе с REST API критически важно правильно обрабатывать ошибки и таймауты, чтобы приложение оставалось стабильным:

async function fetchWithTimeout(url, options = {}, timeout = 5000) {
  const controller = new AbortController();
  const id = setTimeout(() => controller.abort(), timeout);
  const response = await fetch(url, { ...options, signal: controller.signal });
  clearTimeout(id);
  if (!response.ok) throw new Error(`HTTP ошибка: ${response.status}`);
  return response.json();
}

Аутентификация и авторизация

REST API часто требует токенов или ключей для доступа. В Fresh рекомендуется использовать серверные обработчики для хранения секретов, чтобы ключи не попадали на клиент:

export const handler: Handlers = {
  async GET(req) {
    const API_KEY = Deno.env.get("API_KEY");
    const response = await fetch("https://api.example.com/secure-data", {
      headers: { "Authorization": `Bearer ${API_KEY}` },
    });
    const data = await response.json();
    return new Response(JSON.stringify(data), {
      headers: { "Content-Type": "application/json" },
    });
  },
};

Кэширование и оптимизация запросов

Fresh позволяет использовать ETag, Last-Modified и другие HTTP-заголовки для кэширования ответов от API. Кроме того, можно внедрять собственные стратегии кэширования на сервере:

const usersCache = new Map();

export const handler: Handlers = {
  async GET(req) {
    if (usersCache.has("users")) {
      return new Response(JSON.stringify(usersCache.get("users")), {
        headers: { "Content-Type": "application/json" },
      });
    }
    const response = await fetch("https://api.example.com/users");
    const data = await response.json();
    usersCache.set("users", data);
    return new Response(JSON.stringify(data), {
      headers: { "Content-Type": "application/json" },
    });
  },
};

Такой подход снижает нагрузку на внешние сервисы и ускоряет рендеринг страниц.

Вывод

Fresh сочетает серверный рендеринг и клиентскую интерактивность через острова, что позволяет эффективно интегрироваться с REST API. Использование серверных обработчиков, встроенных инструментов для запросов, форм и кэширования делает взаимодействие с внешними сервисами безопасным и производительным.