Minimal F# web framework

Web apps in F#,
light as a firefly.

Firefly is a tiny web framework built straight on Kestrel. Responses go right to the PipeWriter. Idiomatic, composable, fast. A full app in about eight lines.

Get startedStar on GitHub1.3k
$dotnet add package Firefly.Server
the Firefly.Server package
Program.fs
open Firefly
Route.start
|> Route.get "/" (fun _ -> task {
return Response.text "Hello, firefly!" })
|> Route.get "/todos/%i" getTodo
|> App.run

Built on raw Kestrel

Text responses write straight to the PipeWriter, with no middleware tax and no reflection. The shortest path from request to bytes.

Reads like F#

Compose routes with |>. Pattern-match params, group and nest, drop in middleware inline. Idiomatic, declarative, no ceremony.

Minimal by design

One package, a tiny surface area, instant startup. Learn it in an afternoon, keep it for years. Only what you need, nothing you don't.

Typed route paramsTask-based handlersBuilt-in JSONHTTP + gRPCFlame validationJWT middlewareDI via ServiceConsole loggingHot reload
// the whole app

Eight lines to a running service.

Typed route params, task-based handlers, JSON in and out, JWT middleware, and DI, all from the same composable pipeline. Here's a real todo API.

Routes.fs
let routes =
Route.start
|> Route.get "/todos/%i" getTodo
|> Route.post "/todos" createTodo
|> Route.group "/api" (fun api ->
api
|> Route.middleware (Jwt.defaults key |> Jwt.validate)
|> Route.get "/health" (fun _ -> task { return Response.text "ok" }))
Greeter.fs
open Firefly
open Grpc.Core
// a service generated from greet.proto
let greeter = grpcService "greet.Greeter" {
unary "SayHello" (fun (req: HelloRequest) _ctx -> task {
return HelloReply(Message = $"Hello, {req.Name}!")
}})
serverStream "SayHelloStream" (fun req writer _ctx -> task {
for i in 1..5 do do! writer.WriteAsync(reply i)
}})
}
App.defaults
|> App.grpc greeter // gRPC
|> App.run routes // + the HTTP routes
// http + grpc

Not just HTTP. gRPC too.

Define a service with the grpcService builder: unary and server-streaming methods, typed from your .proto. Register it on the same pipeline as your routes: one Kestrel server speaks both protocols.

  • Unary and server-streaming methods
  • Shares DI, config, and the same port as HTTP
  • Strongly-typed messages generated from .proto
Read the gRPC guide
// benchmarks

Fast where it counts.

Response time and allocations measured in-process with BenchmarkDotNet on an Apple M4 Pro, .NET 10. Lower is better.

JSONPlaintext
µsKBµsKB
Firefly45.53.5544.03.46
ASP.NET Core46.33.5841.93.14
Giraffe45.53.8242.13.47
Saturn46.33.8241.33.47
Falco45.53.8342.03.22
Oxpecker47.93.7042.03.25

On par with hand-written ASP.NET Core and the established F# frameworks, with the lowest JSON allocations of the F# options. Reproduce it: dotnet run -c Release --project benchmarks/Firefly.Benchmarks.

// the landscape

How Firefly compares.

FireflyGiraffeSaturnFalcoOxpecker
Built directly onKestrelASP.NETASP.NETASP.NETASP.NET
Hello world8 lines~15~12~10~10
API stylePipelineHandlersCE / DSLHandlersPipeline
Typed params/%iroutefscanmanualroutef
StartupInstantFastHeavierFastFast

A rough orientation, not a scorecard. Every framework here is excellent. Numbers are illustrative.

// community

Loved by F# developers.

“The pipeline composes exactly the way we already think in F#. Routes, middleware, and DI read top to bottom, and the JSON path is genuinely lean on allocations. No magic to unlearn.”
RCRodrigo CoutoStaff Engineer @ Stone
“Eight lines and I had a JSON API with JWT auth. No reflection, no hidden middleware. I can read the entire request path end to end. That’s rare.”
AGAllan GarcezSenior Software Engineer @ Deel
“It feels as light as the F# it’s written in: instant startup, a surface area that fits in my head, and HTTP and gRPC from the very same server.”
RARodrigo AndradePrincipal Engineer @ VTEX
Firefly

Ship your next F# app on Firefly.

One package. Eight lines. Kestrel speed. Start now and have something running before your coffee's cold.

$dotnet add package Firefly.Server
Read the docsView on GitHub