Service HTTP
Flow compose les réponses HTML avec les API Web et peut s’intégrer à un serveur Node classique.
Utiliser serve()#
serve() prend une requête, une fonction de page et un adaptateur. Il encode le flux et compose les en-têtes de la réponse.
import { serve } from "@vincle/flow/http";import { NativeAdapter } from "@vincle/flow/adapters";
Bun.serve({ port: 3000, async fetch(req) { return serve(req, () => <Page />, NativeAdapter); },});Signature#
La fonction retourne une Response utilisable avec un routeur compatible ou un serveur Bun.
serve( req: Request, page: (negotiation: Negotiation) => JSX.Element, adapter: StreamingAdapter, options?: FlowOptions & ResponseInit & { negotiate?: Negotiate; mode?: "full" | "fragment"; },): Promise<Response>Options#
Configurez la négociation, les en-têtes, les délais, le signal d’annulation et onError. Le signal de la requête est transmis au rendu.
Utiliser http de Node.js#
Avec node:http, transmettez le flux au socket et reliez la fermeture de la connexion au signal d’annulation pour arrêter les fragments.
import { Slot, Defer, renderToStream } from "@vincle/flow";import { NativeAdapter } from "@vincle/flow/adapters";import http from "node:http";
declare function fetchComments(): Promise<{ text: string }[]>;
async function Comments() { const items = await fetchComments(); return ( <ul> {items.map((c) => ( <li>{c.text}</li> ))} </ul> );}
function Page() { return ( <html> <body> <h1>My page</h1> <Slot name="comments"> <p>Loading comments…</p> </Slot> <Defer target="comments">{() => <Comments />}</Defer> </body> </html> );}
http .createServer(async (_req, res) => { const stream = renderToStream(() => <Page />, NativeAdapter);
// Shell flushes first; deferred fragments follow as they resolve. res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Transfer-Encoding": "chunked", });
for await (const chunk of stream) { res.write(chunk); } res.end(); }) .listen(3000);
console.log("Listening on http://localhost:3000");Négociation HTMX#
negotiateHtmx reconnaît la cible demandée par HTMX et renseigne les en-têtes de variation nécessaires aux caches.
import { serve, negotiateHtmx } from "@vincle/flow/http";import { HtmxAdapter } from "@vincle/flow/adapters";
Bun.serve({ port: 3000, async fetch(req) { return serve(req, () => <Page />, HtmxAdapter, { negotiate: negotiateHtmx, }); },});Négociation personnalisée#
Définissez une fonction de négociation pour vos propres en-têtes ou conventions de requête.
import type { Negotiate } from "@vincle/flow";
const negotiateMyLib: Negotiate = (req) => { const fragment = req.headers.get("X-Fragment-Id") ?? undefined; return { headers: { Vary: "X-Fragment-Id" }, target: fragment, };};Mode document et mode fragment#
En mode document, la réponse comprend la structure complète. En mode fragment, seul le contenu différé explicitement ciblé est renvoyé ; la fermeture globale du document est omise.
serve(req, () => <Page />, NativeAdapter, { mode: "fragment" });