Fresh — это современный фреймворк для создания веб-приложений на базе Deno, который отличается высокой производительностью и минимальной зависимостью от внешних библиотек. В отличие от традиционных SPA, Fresh использует регенерацию страниц на сервере без необходимости бандлить код для клиента, что снижает нагрузку на сеть и ускоряет время первой загрузки. Центральное понятие Fresh — это ресурсы и острова (islands), которые позволяют интерактивным компонентам существовать внутри статической разметки без потери производительности.
Интеграция с 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, снижая количество сетевых запросов на клиенте и обеспечивая безопасное хранение ключей и токенов.
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>
);
}
Ключевые моменты:
useState обеспечивает
реактивность без полноценного SPA-фреймворка.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. Использование серверных обработчиков, встроенных инструментов для запросов, форм и кэширования делает взаимодействие с внешними сервисами безопасным и производительным.