Frank.Datastar
Datastar SSE integration for Frank web framework with F# computation expression support
Install
| Product | Versions |
|---|---|
| .NET |
README
Frank
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
Features
WebHostBuilder- computation expression for configuringWebHostResourceBuilder- 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
Builderwith 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 DOMDatastar.patchSignals- Update client-side signalsDatastar.removeElement- Remove elements by CSS selectorDatastar.executeScript- Execute JavaScript on the clientDatastar.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 SDK 8.0 or higher
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
Changes from Previous Version
Adoption Assessment
Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.
Related Guides & Workflows
Learn how to evaluate, integrate, or audit this package in your .NET project.