Асинхронные middleware в Fresh строятся вокруг минималистичной, но выразительной модели обработки запросов, унаследованной от экосистемы Deno и философии Web Standards. Middleware в Fresh — это функции, которые встраиваются в жизненный цикл HTTP-запроса и позволяют выполнять асинхронную логику до передачи управления обработчику маршрута или после получения ответа.
Middleware представляет собой асинхронную функцию, принимающую
контекст запроса и функцию next, которая передаёт
управление следующему middleware или конечному обработчику.
import { MiddlewareHandlerContext } from "$fresh/server.ts";
export async function handler(
req: Request,
ctx: MiddlewareHandlerContext,
) {
return await ctx.next();
}
Ключевая особенность — строгая ориентация на стандартный объект
Request и возвращаемый Response. Middleware не
оперирует абстракциями уровня фреймворка HTTP-сервера, а работает
напрямую с web-API.
Каждый middleware является асинхронным по определению. Это позволяет:
Управление потоком основано на явном вызове ctx.next().
Пока next не вызван, дальнейшая цепочка не выполняется.
const response = await ctx.next();
return response;
Такой подход даёт полный контроль над тем, когда и будет ли выполнен следующий шаг.
Асинхронность позволяет использовать middleware как «обёртку» вокруг основной логики.
console.log("before handler");
const response = await ctx.next();
return response;
Типичные задачи:
Request через ctx.state.const response = await ctx.next();
response.headers.set("X-Custom", "value");
return response;
Используется для:
ctx.statectx.state — общий объект состояния, передаваемый между
middleware и конечным обработчиком. Он идеально подходит для хранения
данных, полученных асинхронно.
ctx.state.user = await getUserFromSession(req);
В обработчике маршрута:
const user = ctx.state.user;
Состояние существует только в рамках одного запроса и не требует синхронизации.
Middleware в Fresh применяются иерархически, на основе файловой структуры:
routes/_middleware.ts — глобальный middleware;routes/admin/_middleware.ts — применяется только к
/admin/*;Асинхронные middleware образуют стек вызовов:
next)next)next)next)Это поведение аналогично стеку вызовов и позволяет реализовывать сложную логику без дополнительных инструментов.
Ошибки перехватываются стандартным механизмом
try/catch.
try {
return await ctx.next();
} catch (err) {
return new Response("Internal Error", { status: 500 });
}
Такой middleware может выступать в роли глобального error boundary, перехватывая исключения из любых нижележащих обработчиков.
Важно, что ошибки, возникшие в асинхронном коде, корректно пробрасываются вверх по цепочке, если не были обработаны.
Middleware не обязан вызывать ctx.next(). Это позволяет
полностью остановить обработку запроса.
if (!authorized) {
return new Response("Unauthorized", { status: 401 });
}
Подобное поведение используется для:
Асинхронность middleware позволяет запускать фоновые задачи, не блокируя ответ:
ctx.next().then(() => {
logRequestAsync(req);
});
При этом важно учитывать, что жизненный цикл запроса в Deno может завершиться до окончания фоновой задачи, если она не удерживает event loop.
Fresh предоставляет строгую типизацию через
MiddlewareHandlerContext, что особенно важно при
асинхронной передаче данных через ctx.state. Это снижает
вероятность ошибок при доступе к данным, установленным в предыдущих
middleware.
interface State {
user?: User;
}
export async function handler(
req: Request,
ctx: MiddlewareHandlerContext<State>,
) {
ctx.state.user = await fetchUser();
return await ctx.next();
}
Асинхронные middleware в Fresh:
Минимализм API в сочетании с асинхронной моделью делает middleware в Fresh мощным инструментом для построения серверной логики без избыточной абстракции.