---
title: "Service HTTP"
headingIds:
  [
    "http-serving",
    "using-serve",
    "signature",
    "options",
    "using-node-js-http",
    "htmx-negotiation",
    "custom-negotiation",
    "full-page-vs-fragment-mode",
  ]
---

# 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.

```tsx
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.

```tsx
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.

```tsx
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.

```tsx
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.

```tsx
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.

```tsx
serve(req, () => <Page />, NativeAdapter, { mode: "fragment" });
```
