Skip to main content
net8.0net9.0net10.0

Datastar SSE integration for Frank web framework with F# computation expression support

Published
Feb 6, 2026
8mo ago
Total Downloads
2.0K
1,965

Install

dotnet add package Frank.Datastar --version 7.0.0-build.0
ProductVersions
.NET
net10.0net8.0net9.0

README

Frank

NuGet Version GitHub Release Date Build status

F# computation expressions, or builders, for configuring the Microsoft.AspNetCore.Hosting.IWebHostBuilder and defining routes for HTTP resources using Microsoft.AspNetCore.Routing.

This project was inspired by @filipw's Building Microservices with ASP.NET Core (without MVC).


Packages

Package Description NuGet
Frank Core computation expressions for WebHost and routing NuGet
Frank.Auth Resource-level authorization extensions NuGet
Frank.Datastar Datastar SSE integration for reactive hypermedia NuGet
Frank.Analyzers F# Analyzers for compile-time error detection NuGet

Features

  • WebHostBuilder - computation expression for configuring WebHost
  • ResourceBuilder - computation expression for configuring resources (routing)
  • No pre-defined view engine - use your preferred view engine implementation, e.g. Falco.Markup, Oxpecker.ViewEngine, or Hox
  • Easy extensibility - just extend the Builder with your own methods!

Basic Example

module Program

open System.IO
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Http
open Microsoft.AspNetCore.Routing
open Microsoft.AspNetCore.Routing.Internal
open Microsoft.Extensions.DependencyInjection
open Microsoft.Extensions.Logging
open Frank
open Frank.Builder

let home =
    resource "/" {
        name "Home"

        get (fun (ctx:HttpContext) ->
            ctx.Response.WriteAsync("Welcome!"))
    }

[<EntryPoint>]
let main args =
    webHost args {
        useDefaults

        logging (fun options-> options.AddConsole().AddDebug())

        plugWhen isDevelopment DeveloperExceptionPageExtensions.UseDeveloperExceptionPage
        plugWhenNot isDevelopment HstsBuilderExtensions.UseHsts

        plugBeforeRouting HttpsPolicyBuilderExtensions.UseHttpsRedirection
        plugBeforeRouting StaticFileExtensions.UseStaticFiles

        resource home
    }

    0

Middleware Pipeline

Frank provides two middleware operations with different positions in the ASP.NET Core pipeline:

Request → plugBeforeRouting → UseRouting → plug → Endpoints → Response

plugBeforeRouting

Use for middleware that must run before routing decisions are made:

  • HttpsRedirection - redirect before routing
  • StaticFiles - serve static files without routing overhead
  • ResponseCompression - compress all responses
  • ResponseCaching - cache before routing
webHost args {
    plugBeforeRouting HttpsPolicyBuilderExtensions.UseHttpsRedirection
    plugBeforeRouting StaticFileExtensions.UseStaticFiles
    resource myResource
}

plug

Use for middleware that needs routing information (e.g., the matched endpoint):

  • Authentication - may need endpoint metadata
  • Authorization - requires endpoint to check policies
  • CORS - may use endpoint-specific policies
webHost args {
    plug AuthenticationBuilderExtensions.UseAuthentication
    plug AuthorizationAppBuilderExtensions.UseAuthorization
    resource protectedResource
}

Conditional Middleware

Both plugWhen and plugWhenNot run in the plug position (after routing):

webHost args {
    plugWhen isDevelopment DeveloperExceptionPageExtensions.UseDeveloperExceptionPage
    plugWhenNot isDevelopment HstsBuilderExtensions.UseHsts
    resource myResource
}

Conditional Before-Routing Middleware

Both plugBeforeRoutingWhen and plugBeforeRoutingWhenNot run in the plugBeforeRouting position (before routing):

let isDevelopment (app: IApplicationBuilder) =
    app.ApplicationServices
        .GetService<IWebHostEnvironment>()
        .IsDevelopment()

webHost args {
    // Only redirect to HTTPS in production
    plugBeforeRoutingWhenNot isDevelopment HttpsPolicyBuilderExtensions.UseHttpsRedirection

    // Only serve static files locally in development (CDN in production)
    plugBeforeRoutingWhen isDevelopment StaticFileExtensions.UseStaticFiles

    resource myResource
}

Frank.Auth

Frank.Auth provides resource-level authorization for Frank applications, integrating with ASP.NET Core's built-in authorization infrastructure.

Installation

dotnet add package Frank.Auth

Protecting Resources

Add authorization requirements directly to resource definitions:

open Frank.Builder
open Frank.Auth

// Require any authenticated user
let dashboard =
    resource "/dashboard" {
        name "Dashboard"
        requireAuth
        get (fun ctx -> ctx.Response.WriteAsync("Welcome to Dashboard"))
    }

// Require a specific claim
let adminPanel =
    resource "/admin" {
        name "Admin"
        requireClaim "role" "admin"
        get (fun ctx -> ctx.Response.WriteAsync("Admin Panel"))
    }

// Require a role
let engineering =
    resource "/engineering" {
        name "Engineering"
        requireRole "Engineering"
        get (fun ctx -> ctx.Response.WriteAsync("Engineering Portal"))
    }

// Reference a named policy
let reports =
    resource "/reports" {
        name "Reports"
        requirePolicy "CanViewReports"
        get (fun ctx -> ctx.Response.WriteAsync("Reports"))
    }

// Compose requirements (AND semantics — all must pass)
let sensitive =
    resource "/api/sensitive" {
        name "Sensitive"
        requireAuth
        requireClaim "scope" "admin"
        requireRole "Engineering"
        get (fun ctx -> ctx.Response.WriteAsync("Sensitive data"))
    }

Application Wiring

Configure authentication and authorization services using Frank's builder syntax:

[<EntryPoint>]
let main args =
    webHost args {
        useDefaults

        useAuthentication (fun auth ->
            // Configure your authentication scheme here
            auth)

        useAuthorization

        authorizationPolicy "CanViewReports" (fun policy ->
            policy.RequireClaim("scope", "reports:read") |> ignore)

        resource dashboard
        resource adminPanel
        resource reports
    }
    0

Authorization Patterns

Pattern Operation Behavior
Authenticated user requireAuth 401 if unauthenticated, 200 if authenticated
Claim (single value) requireClaim "type" "value" 403 if claim missing or wrong value
Claim (multiple values) requireClaim "type" ["a"; "b"] 200 if user has any listed value (OR)
Role requireRole "Admin" 403 if user not in role
Named policy requirePolicy "PolicyName" Delegates to registered policy
Multiple requirements Stack multiple require* AND semantics — all must pass
No requirements (default) Publicly accessible, zero overhead

Frank.Datastar

Frank.Datastar provides seamless integration with Datastar, enabling reactive hypermedia applications using Server-Sent Events (SSE).

Installation

dotnet add package Frank.Datastar

Example

open Frank.Builder
open Frank.Datastar

let updates =
    resource "/updates" {
        name "Updates"

        datastar (fun ctx -> task {
            // SSE stream starts automatically
            do! Datastar.patchElements "<div id='status'>Loading...</div>" ctx
            do! Task.Delay(500)
            do! Datastar.patchElements "<div id='status'>Complete!</div>" ctx
        })
    }

// With explicit HTTP method
let submit =
    resource "/submit" {
        name "Submit"

        datastar HttpMethods.Post (fun ctx -> task {
            let! signals = Datastar.tryReadSignals<FormData> ctx
            match signals with
            | ValueSome data ->
                do! Datastar.patchElements $"<div id='result'>Received: {data.Name}</div>" ctx
            | ValueNone ->
                do! Datastar.patchElements "<div id='error'>Invalid data</div>" ctx
        })
    }

Available Operations

  • Datastar.patchElements - Update HTML elements in the DOM
  • Datastar.patchSignals - Update client-side signals
  • Datastar.removeElement - Remove elements by CSS selector
  • Datastar.executeScript - Execute JavaScript on the client
  • Datastar.tryReadSignals<'T> - Read and deserialize signals from request

Each operation also has a WithOptions variant for advanced customization.


Frank.Analyzers

Frank.Analyzers provides compile-time static analysis to catch common mistakes in Frank applications.

Installation

dotnet add package Frank.Analyzers

Available Analyzers

FRANK001: Duplicate HTTP Handler Detection

Detects when multiple handlers for the same HTTP method are defined on a single resource. Only the last handler would be used at runtime, so this is almost always a mistake.

// This will produce a warning:
resource "/example" {
    name "Example"
    get (fun ctx -> ctx.Response.WriteAsync("First"))   // Warning: FRANK001
    get (fun ctx -> ctx.Response.WriteAsync("Second"))  // This one takes effect
}

IDE Integration

Frank.Analyzers works with:

  • Ionide (VS Code)
  • Visual Studio with F# support
  • JetBrains Rider

Warnings appear inline as you type, helping catch issues before you even compile.


Building

Make sure the following requirements are installed in your system:

dotnet build

Sample Applications

The sample/ directory contains several example applications:

Sample Description
Sample Basic Frank application
Frank.Datastar.Basic Datastar integration with minimal HTML
Frank.Datastar.Hox Datastar with Hox view engine
Frank.Datastar.Oxpecker Datastar with Oxpecker.ViewEngine
Frank.Falco Frank with Falco.Markup
Frank.Giraffe Frank with Giraffe.ViewEngine
Frank.Oxpecker Frank with Oxpecker.ViewEngine

License

Apache 2.0

Changes from Previous Version

No structural changes between v6.5.0 and v7.0.0-build.0.

Adoption Assessment

Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.

This version is marked prerelease. Validate its API and upgrade path before production use.Compatibility metadata lists 3 target frameworks; confirm the exact target used by your application.

Related Guides & Workflows

Learn how to evaluate, integrate, or audit this package in your .NET project.