Skip to content

F# HTTP backends

The hydration example uses Giraffe for its HTTP server. The Oxpecker and Suave companion sources below serve the same shared page and browser bundle. The component, codec and hydration entry stay in examples/Ranvier.Hydration; each adapter translates the request into props and sends the rendered document as HTML.

Build the shared browser bundle

From the repository root, with the .NET SDK from global.json and Node 26 or newer:

dotnet tool restore
npm --prefix examples/Ranvier.Hydration ci
npm --prefix examples/Ranvier.Hydration run build

This writes examples/Ranvier.Hydration/dist, including the browser entry at assets/client.js and style.css. All three servers use those assets. The document's relative asset URLs work at the root of each local server and under the documentation's /Ranvier/examples/hydration/ path.

HydrationPage.fromQuery greeting start produces the request props. An empty greeting defaults to Hello from F#; greetings are limited to 200 UTF-16 code units. A missing, invalid or out-of-range start value defaults to 3; accepted values are 0 through 1000000. HydrationPage.render props renders the page and embeds those same props for hydration.

Giraffe

The main example uses Giraffe 8.3.0. Its handler passes the rendered string to htmlString:

let page: HttpHandler =
    fun next ctx ->
        let props =
            HydrationPage.fromQuery
                (string ctx.Request.Query["greeting"])
                (string ctx.Request.Query["start"])

        htmlString (HydrationPage.render props) next ctx

Run the complete Giraffe source:

npm --prefix examples/Ranvier.Hydration start

Open the local Giraffe page. See Giraffe's documentation for its handler composition and response helpers.

Oxpecker

Oxpecker's EndpointHandler receives the context directly. The adapter uses Oxpecker 2.1.1:

let page: EndpointHandler =
    fun ctx ->
        let props =
            HydrationPage.fromQuery
                (ctx.TryGetQueryValue "greeting" |> Option.defaultValue "")
                (ctx.TryGetQueryValue "start" |> Option.defaultValue "")

        ctx.Response.ContentType <- "text/html; charset=utf-8"
        ctx.Response.WriteAsync (HydrationPage.render props)

The complete Oxpecker Program.fs registers routing and Oxpecker, serves /assets through ASP.NET Core static-file middleware, and exposes the stylesheet separately. Its small project references the shared hydration project:

dotnet run --project docs/samples/server-backends/Oxpecker

Open the local Oxpecker page. When adapting the source to an existing ASP.NET Core F# project, add the package with dotnet add package Oxpecker --version 2.1.1 and reference the shared page project. See the official Oxpecker guide for endpoint routing, query values and HTML responses.

Suave

Suave's WebPart reads query parameters from the request and returns a response through its combinators. The adapter uses Suave 3.7.2:

let page: WebPart =
    fun ctx ->
        let query name =
            match ctx.request.queryParam name with
            | Choice1Of2 value -> value
            | Choice2Of2 _ -> ""

        let props = HydrationPage.fromQuery (query "greeting") (query "start")
        (OK (HydrationPage.render props) >=> setMimeType "text/html; charset=utf-8") ctx

The complete Suave Program.fs serves the rendered page at /, allows the built /assets/ files and stylesheet, and binds to the loopback address:

dotnet run --project docs/samples/server-backends/Suave

Open the local Suave page. When adapting the source to an existing F# project, add Suave with dotnet add package Suave --version 3.7.2 and reference the shared page project. Suave documents request and response handling and static files.

Check the server and browser boundary

Request a URL with a greeting and starting count, then view its HTML source: both values are already present before JavaScript runs. Change the count in the browser, then reload; the server supplies the query's starting value again. The local interaction is owned by the browser; these examples do not store counter changes on the server.

The companion programs accept an optional absolute path to the built dist directory as their first application argument. This is useful when copying the source outside this checkout. Both serve the live route before any static HTML, so the exported snapshot cannot replace a request-specific page.

The hosted preview is a build-time snapshot on GitHub Pages. Running one of these local servers demonstrates fresh HTML for each request. See page declarations and state transfer for the shared Ranvier APIs.

Edit this page