Frank 7.3.3

dotnet add package Frank --version 7.3.3
                    
NuGet\Install-Package Frank -Version 7.3.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Frank" Version="7.3.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Frank" Version="7.3.3" />
                    
Directory.Packages.props
<PackageReference Include="Frank" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Frank --version 7.3.3
                    
#r "nuget: Frank, 7.3.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Frank@7.3.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Frank&version=7.3.3
                    
Install as a Cake Addin
#tool nuget:?package=Frank&version=7.3.3
                    
Install as a Cake Tool

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

Installation

dotnet add package Frank

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
}

Content Negotiation

The negotiate { } computation expression performs real per-media-type dispatch: each accepts registers an independent representation, and the one matching the request's Accept header (by RFC 9110 quality and specificity rules) is the only one whose handler runs. Nothing matching means 406 Not Acceptable, and every response carries Vary: Accept.

resource "/products/{id}" {
    get (negotiate {
        accepts "application/json" (fun ctx -> task {
            do! ctx.Response.WriteAsJsonAsync(product)
        })
        accepts "text/html" (fun ctx -> task {
            do! ctx.Response.WriteAsync($"<h1>{product.Name}</h1>")
        })
    })
}

Frank has several companion packages that build on this core: Frank.Auth (authorization), Frank.OpenApi (OpenAPI generation), Frank.JsonHome (hypermedia discovery), Frank.Datastar (reactive SSE), Frank.Rdf (linked data), and Frank.Analyzers (compile-time checks).

See the project repository for the complete guide and sample applications.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (6)

Showing the top 5 NuGet packages that depend on Frank:

Package Downloads
Frank.Datastar

Datastar SSE integration for Frank web framework with F# computation expression support. Native SSE implementation with no external dependencies, supports .NET 8.0/9.0/10.0.

Frank.Auth

Resource- and handler-level authorization extensions for Frank web framework

Frank.OpenApi

OpenAPI document generation extensions for Frank web framework

Frank.Validation

Hand-authored SHACL Core validation for Frank resources, built on Frank.Rdf

Frank.Alps

Hand-authored ALPS profile documents for Frank resources

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
7.3.3 185 8/11/2026
7.3.2 166 8/10/2026
7.2.1 194 6/22/2026
7.2.0 243 2/10/2026
7.1.0 158 2/8/2026
7.0.0-build.0 83 2/6/2026
6.5.0 215 2/5/2026
6.4.1 155 2/4/2026
6.4.1-build.0 83 2/4/2026
6.4.0 140 2/2/2026
6.4.0-build.0 84 2/2/2026
6.3.0 544 3/15/2025
6.2.0 5,527 11/18/2020
6.1.0 1,917 6/11/2020
6.0.0 947 6/2/2020
5.0.5 1,115 1/7/2019
Loading failed

### New in 7.3.3 (Released 2026-08-10)
**Frank.Rdf - Async IBufferWriter Streaming**
- **New: `Doc.writeJsonLdAsync doc bufferWriter`** — async overload that writes JSON-LD expanded-form directly to an `IBufferWriter<byte>` (e.g. `HttpResponse.BodyWriter`/`PipeWriter`), without intermediate string allocation or copying. Encodes UTF8 directly to the buffer for maximum efficiency in response streaming. Completes after serialization and flushing to the buffer.
- **Recommended for response streaming:** `writeJsonLdAsync` is the preferred method when serving JSON-LD from a Frank handler over HTTP. Streaming directly to `PipeWriter` avoids `AllowSynchronousIO` requirements and eliminates intermediate buffering layers that `StreamWriter` would introduce.
- **Three serialization options now available:** Use `writeJsonLdAsync` for HTTP responses (most efficient), `writeJsonLd` for flexibility with any `TextWriter`, and `toJsonLd` for testing/debugging.
- **Sample updated:** `sample/Frank.Rdf.Sample` demonstrates `Doc.writeJsonLdAsync` streaming the `application/ld+json` representation directly to the response body via `negotiate { }` content negotiation.
- **Test coverage:** comprehensive test cases for `writeJsonLdAsync` including single and multi-subject documents, language-tagged strings, and round-trip parsing.