Fresh — это современный фреймворк для создания веб-приложений на базе Deno, ориентированный на высокую производительность, нативный SSR (Server-Side Rendering) и минимализм. Основная идея заключается в том, чтобы максимально использовать возможности ES-модулей и сервера без лишних слоёв абстракции, обеспечивая мгновенную отдачу страниц и минимальный размер бандлов.
Fresh построен на трёх ключевых принципах:
В современных приложениях Fresh часто интегрируют с GraphQL, позволяя запрашивать данные на сервере и передавать их в компоненты Islands. GraphQL обеспечивает более гибкое взаимодействие с API по сравнению с REST, позволяя выбирать только необходимые поля и уменьшать объём передаваемых данных.
Для работы с GraphQL в Fresh можно использовать стандартные
fetch вызовы, но чаще подключают легковесные клиенты,
например graphql-request. Основная настройка включает
указание URL GraphQL сервера и при необходимости заголовков
авторизации:
import { GraphQLClient } FROM "https://cdn.skypack.dev/graphql-request";
const client = new GraphQLClient("https://api.example.com/graphql", {
headers: {
Authorization: `Bearer ${Deno.env.get("API_TOKEN")}`,
},
});
export default client;
Запросы в GraphQL описываются в виде строковых литералов. Fresh позволяет выполнять их на стороне сервера при рендеринге страниц:
import client FROM "./client.ts";
const query = `
query GetPosts($limit: Int!) {
posts(LIMIT: $limit) {
id
title
content
}
}
`;
const variables = { LIMIT: 10 };
const data = await client.request(query, variables);
Особенности работы в Fresh:
route handler
или islands компонентах при серверном рендеринге.В архитектуре Islands данные обычно подгружаются на сервере и
передаются интерактивному компоненту через props. Такой
подход позволяет:
Пример Islands компонента с данными из GraphQL:
/** @jsx h */
import { h } from "preact";
import { useState } from "preact/hooks";
export default function PostsIsland({ posts }) {
const [items, setItems] = useState(posts);
return (
<div>
{items.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
</article>
))}
</div>
);
}
На сервере:
import PostsIsland from "../islands/PostsIsland.tsx";
import client from "../graphql/client.ts";
const query = `
query GetPosts {
posts {
id
title
content
}
}
`;
export default async function handler(req, ctx) {
const posts = await client.request(query);
return ctx.render({ posts });
}
Помимо чтения данных, GraphQL поддерживает мутации для изменения состояния на сервере. В Fresh мутации выполняются аналогично запросам, но обычно через обработчики POST-запросов или внутри Islands для интерактивных форм:
const mutation = `
mutation AddPost($title: String!, $content: String!) {
addPost(title: $title, content: $content) {
id
title
content
}
}
`;
const variables = { title: "Новая статья", content: "Содержимое статьи" };
const result = await client.request(mutation, variables);
Важно учитывать:
Для больших приложений с Fresh и GraphQL актуальны следующие приёмы:
GraphQL поддерживает подписки для реального времени через WebSocket. В Fresh это реализуется через отдельный клиент на стороне Islands, так как подписки требуют постоянного соединения и не подходят для серверного рендеринга HTML напрямую. Стандартный подход:
import { useEffect, useState } from "preact/hooks";
import { createClient } from "graphql-ws";
const client = createClient({ url: "wss://api.example.com/graphql" });
export default function LivePostsIsland({ initialPosts }) {
const [posts, setPosts] = useState(initialPosts);
useEffect(() => {
const dispose = client.subscribe(
{ query: "subscription { newPost { id title content } }" },
{
next: data => setPosts(prev => [...prev, data.newPost]),
error: err => console.error(err),
complete: () => console.log("Subscription complete")
}
);
return () => dispose();
}, []);
return (
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
</article>
))}
</div>
);
}
Использование подписок позволяет построить интерактивные и динамичные приложения без полной перезагрузки страницы.
При интеграции GraphQL в Fresh важно соблюдать правила безопасности:
Эти меры обеспечивают безопасное взаимодействие между клиентом и сервером, сохраняя принципы Zero JavaScript by default и Islands Architecture.