Fresh — это современный фреймворк для разработки фронтенда на основе Deno, ориентированный на реактивные интерфейсы без необходимости использования клиентских бандлеров. В основе Fresh лежит концепция SSR (Server-Side Rendering) с минимальной зависимостью от JavaScript на клиенте, что повышает производительность и снижает сложность фронтенд-приложений.
Одним из ключевых элементов работы с Fresh являются обработчики (handlers) — функции, которые управляют жизненным циклом запросов и формируют ответ для пользователя.
Обработчики — это функции, привязанные к определённым маршрутам. Они реализуют логику получения данных, обработки форм, редиректов и других серверных операций. В Fresh обработчик описывается через объект с методами HTTP-запросов:
export const handler = {
GET(req, ctx) {
return new Response("Hello, Fresh!");
},
POST(req, ctx) {
const formData = await req.formData();
const name = formData.get("name");
return new Response(`Hello, ${name}!`);
}
};
req — объект запроса, предоставляет
доступ к параметрам, заголовкам, телу запроса.ctx — контекст маршрута, содержит
параметры URL, состояние сессий и другие метаданные.Методы обработчика должны возвращать Response или промис, который резолвится в Response. Это позволяет легко интегрировать серверные данные с SSR и компонентами Fresh.
export const handler = {
GET(req, ctx) {
const data = fetchDataFromDatabase(ctx.params.id);
return data.then(result => new Response(JSON.stringify(result), {
headers: { "Content-Type": "application/json" }
}));
}
};
export const handler = {
POST(req) {
const form = await req.formData();
const email = form.get("email");
if (!validateEmail(email)) {
return new Response("Invalid email", { status: 400 });
}
saveEmailToDB(email);
return Response.redirect("/thank-you");
}
};
export const handler = {
GET(req) {
if (!userIsLoggedIn(req)) {
return Response.redirect("/login");
}
return new Response("Welcome back!");
}
};
Тестирование серверных функций в Fresh не отличается от обычного тестирования функций Node/Deno. Основные подходы включают:
Юнит-тесты проверяют обработчики на корректную обработку входных данных и формирование ответа:
import { handler } from "./myHandler.js";
import { assertEquals } from "std/testing/asserts.ts";
Deno.test("GET возвращает корректный текст", async () => {
const response = await handler.GET(new Request("http://localhost/"));
const text = await response.text();
assertEquals(text, "Hello, Fresh!");
});
В этом примере создаётся объект Request, аналогичный
стандартному HTTP-запросу, и проверяется, что обработчик возвращает
ожидаемый текст.
Интеграционные тесты эмулируют полный поток запроса и ответа:
import { superoak } from "superoak";
import app from "../main.js"; // приложение Fresh
Deno.test("POST форма сохраняет email и делает редирект", async () => {
const request = await superoak(app);
await request.post("/subscribe")
.send({ email: "user@example.com" })
.expect(302)
.expect("Location", "/thank-you");
});
Ключевое преимущество интеграционных тестов — проверка взаимодействия всех компонентов: маршрутов, обработчиков и сторонних сервисов (например, базы данных).
ctx позволяет
передавать данные между middleware, обработчиками и компонентами,
упрощая управление состоянием.Обработчики тесно связаны с компонентами Fresh:
ctx.render(data), что делает серверный рендеринг полностью
реактивным.Пример интеграции обработчика с компонентом:
export const handler = {
GET(req, ctx) {
const data = fetchData();
return ctx.render({ items: data });
}
};
В компоненте можно использовать полученные данные напрямую:
export default function Page({ data }) {
return (
<ul>
{data.items.map(item => <li>{item.name}</li>)}
</ul>
);
}
Такой подход обеспечивает высокую производительность и предсказуемость работы приложения.