В фреймворке Fresh middleware играет ключевую роль в обработке запросов и управлении потоками данных до попадания на рендеринг компонентов. Правильная типизация middleware позволяет создавать безопасные и предсказуемые приложения, минимизируя ошибки на этапе компиляции и улучшая автокомплит в редакторах кода.
Middleware во Fresh представляют собой функции, которые получают
объект запроса Request, контекст Context и
функцию next. Они могут выполнять следующие действия:
Типичная сигнатура middleware:
import type { MiddlewareHandlerContext } from "$fresh/server.ts";
export function authMiddleware(
req: Request,
ctx: MiddlewareHandlerContext,
next: () => Promise<Response>,
): Promise<Response> {
// Логика авторизации
return next();
}
Ключевой момент: ctx является объектом, содержащим
информацию о текущем запросе и состояние приложения. Его типизация
критически важна для безопасной работы с пользовательскими данными и
расширениями.
Fresh позволяет расширять контекст middleware, добавляя собственные поля. Это удобно для хранения информации о пользователе, токенах или других данных, которые должны быть доступны на последующих этапах обработки запроса.
Пример расширения:
interface CustomContext extends MiddlewareHandlerContext {
user?: { id: string; role: string };
}
export async function authMiddleware(
req: Request,
ctx: CustomContext,
next: () => Promise<Response>,
) {
const token = req.headers.get("Authorization");
if (token) {
ctx.user = { id: "123", role: "admin" }; // пример получения пользователя
}
return next();
}
Типизация контекста позволяет избежать ошибок доступа к полям
user на этапе компиляции и обеспечивает поддержку
автодополнения в редакторах.
Middleware во Fresh можно комбинировать в цепочки, где каждый слой
обрабатывает запрос и передает управление следующему через
next(). Типизация функции next критически
важна, так как она возвращает Promise<Response>.
Ошибки в типах могут привести к некорректной работе цепочки или неверной
обработке ответов.
Пример составной middleware:
export async function loggingMiddleware(
req: Request,
ctx: MiddlewareHandlerContext,
next: () => Promise<Response>,
) {
console.log(`${req.method} ${req.url}`);
const response = await next();
console.log(`Response status: ${response.status}`);
return response;
}
export async function combinedMiddleware(
req: Request,
ctx: CustomContext,
next: () => Promise<Response>,
) {
await loggingMiddleware(req, ctx, async () => {
return authMiddleware(req, ctx, next);
});
}
Здесь каждый middleware сохраняет строгую типизацию контекста и возвращаемого значения, что позволяет безопасно комбинировать их без потери автодополнения и проверки типов.
Для сложных приложений важно явно указывать типы данных, получаемых в запросе и отправляемых в ответе. Fresh не накладывает жестких ограничений на это, но правильная типизация позволяет избежать runtime ошибок:
interface LoginRequestBody {
username: string;
password: string;
}
interface LoginResponseBody {
token: string;
expiresAt: string;
}
export async function loginMiddleware(
req: Request,
ctx: MiddlewareHandlerContext,
next: () => Promise<Response>,
) {
const body: LoginRequestBody = await req.json();
if (body.username === "admin" && body.password === "123") {
const responseBody: LoginResponseBody = { token: "abc", expiresAt: new Date().toISOString() };
return new Response(JSON.stringify(responseBody), { status: 200 });
}
return next();
}
Использование интерфейсов для типов данных запроса и ответа повышает читаемость и надежность кода.
ctx, особенно при расширении
пользовательскими данными.Promise<Response>
для middleware.next.interface CustomCtx extends MiddlewareHandlerContext {
session?: string;
}
export async function exampleMiddleware(
req: Request,
ctx: CustomCtx,
next: () => Promise<Response>,
): Promise<Response> {
ctx.session = "active";
return next();
}
await firstMiddleware(req, ctx, async () => {
return secondMiddleware(req, ctx, next);
});
Строгая типизация middleware во Fresh обеспечивает предсказуемость приложения, улучшает автодополнение и предотвращает класс ошибок, связанных с неверной обработкой контекста или данных запроса.