OpenAPI спецификация

Fresh — это современный фреймворк для разработки веб-приложений на JavaScript и TypeScript, ориентированный на минимизацию времени загрузки страниц и оптимизацию производительности за счёт рендеринга на стороне сервера. В основе Fresh лежит концепция islands architecture, где интерактивные компоненты загружаются на клиент только по необходимости, а остальной контент остаётся статическим.

Основные принципы работы

  1. Рендеринг на сервере (SSR) В Fresh большинство страниц рендерится на сервере, что позволяет получать полностью сформированный HTML без лишних скриптов на начальном этапе. Это улучшает SEO и ускоряет первую отрисовку страницы (Time to First Paint).

  2. Islands architecture Каждый интерактивный компонент на странице изолирован в виде «острова». Это означает:

    • Минимальный JavaScript для каждой интерактивной части.
    • Загрузка только нужных скриптов, а не всего приложения целиком.
    • Улучшенная производительность и уменьшение объёма передаваемых данных.
  3. Отсутствие сборщика по умолчанию Fresh использует возможности нативного ES-модулей в браузере. Это сокращает время сборки и упрощает конфигурацию проекта. Каждый модуль импортируется напрямую через <script type="module">.

Структура проекта

Типичный проект на Fresh имеет следующие элементы:

  • routes/ — каталог с маршрутами. Каждый файл соответствует URL-пути.
  • components/ — интерактивные компоненты (острова).
  • islands/ — папка для компонентов, которые будут подгружаться на клиент.
  • static/ — статические файлы: изображения, CSS, шрифты.
  • main.ts или main.js — точка входа приложения.

Создание страницы

Страница создаётся как модуль, экспортирующий функцию handler или JSX-компонент:

/** routes/index.tsx */
import { h } from "preact";

export default function Home() {
  return (
    <div>
      <h1>Главная страница</h1>
      <p>Контент загружается с сервера.</p>
    </div>
  );
}

Если нужно сделать интерактивный компонент, его помещают в папку islands:

/** islands/Counter.tsx */
import { useState } from "preact/hooks";

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onCl ick={() => setCount(count + 1)}>
      Кликнули {count} раз
    </button>
  );
}

И подключают на странице:

import Counter from "../islands/Counter.tsx";

export default function Home() {
  return (
    <div>
      <h1>Главная страница</h1>
      <Counter />
    </div>
  );
}

Работа с данными

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

/** routes/posts.tsx */
import { h } from "preact";

export async function handler(req) {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await res.json();
  return new Response(
    JSON.stringify(posts),
    { headers: { "Content-Type": "application/json" } }
  );
}

Далее данные можно использовать в JSX:

export default function Posts({ data }) {
  return (
    <ul>
      {data.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Преимущества использования Fresh

  • Мгновенная первая отрисовка — HTML готов на сервере.
  • Минимальный JavaScript — загружаются только интерактивные острова.
  • Простая структура проекта — отсутствует сложная сборка.
  • Поддержка TypeScript — полная интеграция без дополнительных конфигураций.
  • SEO-дружелюбность — контент доступен без выполнения JS на клиенте.

Взаимодействие с API

Fresh удобно использовать для построения приложений с API на стороне сервера. Можно комбинировать SSR и клиентские запросы к внешним API. Например:

/** routes/api/data.ts */
export async function handler(req) {
  const data = { message: "Привет из API!" };
  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" },
  });
}

На клиенте данные можно получить через fetch:

import { useEffect, useState } from "preact/hooks";

export default function ApiData() {
  const [message, setMessage] = useState("");

  useEffect(() => {
    fetch("/api/data")
      .then(res => res.json())
      .then(data => setMessage(data.message));
  }, []);

  return <p>{message}</p>;
}

Оптимизация производительности

Fresh обеспечивает следующие возможности:

  • Автоматическая оптимизация изображений — встроенные утилиты для сжатия и ресайза.
  • Кэширование страниц и данных — статический рендеринг позволяет использовать CDN.
  • Lazy-loading компонентов — интерактивные острова подгружаются только при необходимости.
  • Минификация и tree-shaking — за счёт использования ES-модулей неиспользуемый код автоматически исключается.

Совместимость с OpenAPI

Для интеграции с OpenAPI можно генерировать типы TypeScript на основе спецификации API. Это обеспечивает:

  • Автодополнение при работе с методами API.
  • Проверку типов на этапе компиляции.
  • Упрощение взаимодействия между фронтендом и бэкендом.
import { paths } from "./openapi-types";

async function getUsers() {
  const response = await fetch("/api/users");
  const data: paths["/users"]["get"]["responses"]["200"]["content"]["application/json"] = await response.json();
  return data;
}

Использование Fresh совместно с OpenAPI позволяет создавать безопасные, быстрые и типизированные веб-приложения, минимизируя ручное написание обёрток для API и уменьшая риск ошибок.