Aller au contenu
Vincle

Chargement…

    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" });