В Fresh middleware — это функции, которые встраиваются в процесс обработки HTTP-запроса между получением запроса сервером и формированием окончательного ответа. Они позволяют централизованно решать сквозные задачи: аутентификацию, логирование, работу с заголовками, локализацию, контроль доступа, модификацию запроса и ответа.
Middleware в Fresh работает на уровне Edge-совместимого сервера Deno и тесно интегрирован с роутингом фреймворка. Ключевая особенность — композиция middleware, то есть последовательное и предсказуемое объединение нескольких middleware в единый конвейер обработки запроса.
Middleware в Fresh представляет собой функцию, принимающую объект
Request и контекст MiddlewareHandlerContext и
возвращающую Response или
Promise<Response>.
import { MiddlewareHandlerContext } from "$fresh/server.ts";
export async function middleware(
req: Request,
ctx: MiddlewareHandlerContext,
) {
return await ctx.next();
}
Ключевые элементы:
req — неизменяемый объект запросаctx.next() — вызов следующего
middleware или обработчика маршрутаMiddleware может:
Responsectx.next()Композиция middleware в Fresh строится по принципу цепочки ответственности. Каждый middleware оборачивает последующий, образуя вложенную структуру вызовов.
Последовательность выполнения выглядит следующим образом:
ctx.next()Это позволяет реализовывать как pre-processing, так и post-processing логики.
Схематично:
Request
↓
Middleware A (до)
↓
Middleware B (до)
↓
Route handler
↑
Middleware B (после)
↑
Middleware A (после)
Response
Fresh поддерживает два уровня middleware:
Располагаются в файле routes/_middleware.ts и
применяются ко всем маршрутам.
// routes/_middleware.ts
export async function middleware(req, ctx) {
console.log(req.method, req.url);
return await ctx.next();
}
Используются для:
Можно определить middleware внутри конкретного маршрута.
// routes/admin/_middleware.ts
export async function middleware(req, ctx) {
if (!isAdmin(req)) {
return new Response("Forbidden", { status: 403 });
}
return await ctx.next();
}
Такой middleware применяется только к маршрутам внутри директории
admin.
Порядок подключения middleware определяется структурой файловой системы:
routes/_middleware.tsroutes/segment/_middleware.tsroutes/segment/sub/_middleware.tsКаждый уровень оборачивает следующий. Это гарантирует детерминированное выполнение без необходимости ручной конфигурации.
Пример структуры:
routes/
├─ _middleware.ts
├─ admin/
│ ├─ _middleware.ts
│ └─ dashboard.tsx
Цепочка вызовов для /admin/dashboard:
admindashboard.tsxMiddleware может полностью контролировать дальнейшее выполнение цепочки.
Если middleware возвращает Response без вызова
ctx.next(), дальнейшие middleware и обработчик маршрута не
выполняются.
if (!authorized) {
return new Response("Unauthorized", { status: 401 });
}
Частый приём — модификация ответа после выполнения следующего обработчика.
const response = await ctx.next();
response.headers.set("X-Powered-By", "Fresh");
return response;
Это позволяет добавлять:
ctx.state — механизм передачи данных между middleware и
обработчиками маршрутов.
ctx.state.user = user;
return await ctx.next();
В обработчике маршрута:
export const handler = {
GET(req, ctx) {
const user = ctx.state.user;
return new Response(`Hello ${user.name}`);
}
};
Особенности:
state живёт только в рамках одного запросаFresh позволяет строго типизировать ctx.state, что
особенно важно при глубокой композиции middleware.
interface State {
user?: User;
locale?: string;
}
export async function middleware(
req: Request,
ctx: MiddlewareHandlerContext<State>,
) {
ctx.state.locale = "ru";
return await ctx.next();
}
Все последующие middleware и обработчики получают доступ к расширенному состоянию с гарантированной типобезопасностью.
Middleware полностью поддерживает асинхронность. Ошибки, выброшенные в любом middleware, всплывают вверх по цепочке.
try {
return await ctx.next();
} catch (err) {
return new Response("Internal Error", { status: 500 });
}
Это позволяет реализовать централизованную обработку ошибок на уровне глобального middleware.
Композиция middleware в Fresh — не просто технический механизм, а архитектурный приём:
Благодаря файловой композиции и строгому порядку выполнения, middleware в Fresh остаётся прозрачным, масштабируемым и легко сопровождаемым даже в крупных проектах.