Создание middleware

Fresh — это современный фреймворк для разработки веб-приложений на базе Deno, который ориентирован на производительность, минимализм и использование стандартных возможностей языка и платформы. Основной подход Fresh заключается в рендеринге страниц на сервере (SSR — Server-Side Rendering) с максимально лёгкой клиентской частью и отсутствием необходимости в бандлерах.

Ключевая особенность Fresh — отсутствие лишнего JavaScript на клиенте по умолчанию. Скрипты подключаются только там, где это действительно нужно, что снижает время загрузки страницы и уменьшает потребление ресурсов. Fresh использует islands architecture: интерактивные элементы приложения (острова) подключаются на страницу как отдельные компоненты, все остальное остаётся статическим HTML.


Middleware в Fresh

Middleware — это функция, которая перехватывает входящие HTTP-запросы и может модифицировать их, перенаправлять или добавлять дополнительные данные перед передачей их на обработку маршрутам или компонентам. В Fresh middleware реализуются через handlers, которые вызываются до рендеринга страницы.

Структура middleware

Middleware в Fresh представляет собой экспортируемую функцию handler, которая принимает объект RequestEvent и возвращает объект Response или передаёт управление следующему обработчику. Основные элементы:

export const handler = async (reqEvent) => {
  // Логика middleware
};

RequestEvent включает:

  • request — объект запроса, аналогичный Request из Fetch API.
  • params — параметры маршрута.
  • url — объект URL запроса.
  • locals — объект для хранения данных между middleware и рендерингом страницы.

Простейший пример middleware

Middleware, который логирует каждый запрос:

export const handler = async (reqEvent) => {
  console.log(`Запрос: ${reqEvent.request.method} ${reqEvent.url.pathname}`);
  return await reqEvent.next(); // Передаём управление следующему обработчику
};

Ключевой момент: метод next() позволяет передавать обработку дальше, иначе запрос будет заблокирован.


Аутентификация через middleware

Middleware часто используется для проверки авторизации пользователей. Например:

export const handler = async (reqEvent) => {
  const token = reqEvent.request.headers.get("Authorization");
  if (!token || token !== "Bearer my-secret-token") {
    return new Response("Unauthorized", { status: 401 });
  }
  return await reqEvent.next();
};

Здесь locals может быть использован для хранения информации о пользователе:

reqEvent.locals.user = { id: 1, name: "Admin" };

Далее эти данные доступны в компонентах и маршрутах приложения.


Редиректы и переадресация

Middleware позволяет выполнять редиректы на основе условий:

export const handler = async (reqEvent) => {
  if (!reqEvent.url.pathname.startsWith("/admin")) {
    return Response.redirect("/login", 302);
  }
  return await reqEvent.next();
};

Таким образом, middleware может контролировать доступ к различным разделам приложения.


Асинхронные операции и взаимодействие с БД

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

export const handler = async (reqEvent) => {
  const userId = reqEvent.request.headers.get("X-User-ID");
  const user = await getUserFromDB(userId); // асинхронная функция
  if (!user) return new Response("Not Found", { status: 404 });
  
  reqEvent.locals.user = user;
  return await reqEvent.next();
};

Все асинхронные операции выполняются до передачи запроса компонентам, что делает SSR полностью подготовленным к отображению актуальных данных.


Комбинация нескольких middleware

Можно подключать несколько middleware на один маршрут. Они выполняются последовательно в порядке подключения:

export const handler = [
  async (reqEvent) => { console.log("Первый middleware"); return await reqEvent.next(); },
  async (reqEvent) => { console.log("Второй middleware"); return await reqEvent.next(); }
];

Такой подход позволяет разделять логику, например, логирование, аутентификацию и подготовку данных к рендерингу.


Использование locals для передачи данных

Объект locals в RequestEvent служит для передачи данных между middleware и страницами. Он не сохраняется между запросами и доступен только во время текущего запроса:

export const handler = async (reqEvent) => {
  reqEvent.locals.theme = "dark";
  return await reqEvent.next();
};

// В компоненте страницы
export default function Page({ data, event }) {
  return <div>Тема: {event.locals.theme}</div>;
}

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


Ограничения и особенности

  1. Middleware не работают на уровне статических файлов.
  2. Они выполняются до рендеринга страницы и могут изменять только входящий запрос или генерировать ответ напрямую.
  3. Объект locals безопасен для передачи данных между middleware и компонентами, но не сохраняется между различными запросами.
  4. Асинхронные операции в middleware влияют на скорость ответа, поэтому критически важно оптимизировать запросы к внешним сервисам.

Итоговая структура middleware в Fresh

  • Экспортируемая функция handler или массив функций.
  • Использование RequestEvent с ключевыми полями: request, url, params, locals.
  • Методы: next() для передачи управления, Response.redirect() для редиректов.
  • Поддержка асинхронного кода и работы с базой данных.
  • Возможность последовательного подключения нескольких middleware для модульной логики.

Middleware в Fresh представляет собой мощный инструмент для контроля запросов, управления доступом, передачи данных и подготовки страниц к серверному рендерингу, при этом сохраняя высокую производительность и минимальную клиентскую нагрузку.