Giraffe: an F# micro framework that plugs into ASP.NET Core
A native functional ASP.NET Core web framework for F# developers.
At a glance
- What is it?
- How the HttpHandler composition model fits into an existing ASP.NET Core pipeline, what the 8.2 security release changed, and where Giraffe stops being the right tool.
- Who is it for?
- Giraffe earns its place by staying small and specific: one handler composition model, mounted inside a host that already solves everything else, licensed under Apache-2.0 and last pushed on 2026-09-28. Read the 8.2.0 release notes before your first upgrade, because the redirect and XML validation changes there are breaking and security motivated.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly F#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Where Giraffe sits relative to ASP.NET Core
Giraffe describes itself as a functional ASP.NET Core micro web framework for building rich web applications, and that wording is worth unpacking because it rules out a whole class of expectations. It is not a standalone server. The documentation is explicit that it is not designed to be a competing web product which can be run standalone like NancyFx or Suave, but rather a lean micro framework which aims to complement ASP.NET Core where it comes short for functional developers. The stated plan is to build on the strong foundation of ASP.NET Core and re-use existing ASP.NET Core building blocks so F# developers can benefit from both worlds.
So the framework occupies a narrow band. Microsoft maintains the Kestrel host, the configuration system, dependency injection, logging, authentication middleware, and the enormous surrounding package ecosystem. Giraffe contributes one thing: a composable request handling model that reads like F# instead of like a class hierarchy. The documentation offers a useful shorthand, describing Giraffe as the functional counter part of the ASP.NET Core MVC framework. If you have written controllers and filters, the translation is that `HttpHandler` values take the place of action methods, and the combinators that stitch handlers together take the place of routing configuration and filters.
That positioning has practical consequences. Hosting, TLS termination, metrics export, OpenAPI integration, and deployment all arrive through the ASP.NET Core side of the pipeline, so a service that adopts Giraffe does not need to solve those problems. The trade is that you cannot lift a Giraffe application out of ASP.NET Core and run it under a different host, because the framework leans on `IApplicationBuilder` and the middleware ordering semantics of the host. Projects arrive at 2,255 stars and 267 forks with an active issue tracker of 45 open items, and the last recorded push on the default branch is dated 2026-09-28, which is well inside a window that counts as active maintenance.
The project is licensed under Apache-2.0, has a dedicated wiki at giraffe.wiki, and ships its own documentation file, release notes file, development guide, security policy, and changelog at the root of the tree. The source lives under `src/` with a parallel `tests/` directory and a `samples/` folder holding runnable applications. A solution file named `Giraffe.slnx` and a `global.json` at the root pin the toolchain expectations for contributors building from source.
The HttpHandler model in practice
Applications are composed of so called `HttpHandler` functions, which the documentation describes as a mixture of Suave's WebParts and ASP.NET Core's middleware. In practice a handler is an F# function from `HttpContext` to `Task`, and two operators do most of the work. `>=>` sequences two handlers so the output of one becomes the input of the next, which is how a request reaches a route after authentication. `>->` maps a value into a handler, which is how you build a handler that depends on a configured value. A third combinator, `choose`, takes a list of handlers and returns the first match, so route tables read as an ordered list rather than a configuration DSL.
Because handlers are ordinary functions, they compose without ceremony. A handler that wraps another is just a function that awaits the inner one, and that property is what makes Giraffe pleasant to test: you can build an `HttpContext`, pass it to a handler, and assert on the result without spinning up a host. It also means middleware and Giraffe handlers share the same signature, so an existing ASP.NET Core middleware and a Giraffe handler can sit next to each other in the same pipeline without adapters.
The minimal hosting example in the documentation shows the whole shape of a service, starting from the route table and ending at `app.Run()`. The two routes are enough to show the pattern: a text response for a health check style endpoint, and a static HTML file for the root path.
let webApp =
choose [
route "/ping" >=> text "pong"
route "/" >=> htmlFile "/pages/index.html" ]The same `webApp` value is then handed to the ASP.NET Core pipeline through `UseGiraffe`, which is the single line that makes this a Giraffe application rather than a plain ASP.NET Core one. The dependency registration happens through `AddGiraffe`, which registers the default handlers that the combinators depend on.
let configureApp (app : IApplicationBuilder) =
// Add Giraffe to the ASP.NET Core pipeline
app.UseGiraffe webApp
let configureServices (services : IServiceCollection) =
// Add Giraffe dependencies
services.AddGiraffe() |> ignoreHandlers also carry routing information in two different ways, which is the source of the most common confusion for newcomers. A full application can use the `choose` and `route` style list above, where routing is a value composed at startup. It can also sit alongside ASP.NET Core's endpoint routing, where the framework registers endpoints on a separate builder and matching happens before the Giraffe handler runs. Both work in the same application, and mixing them is common when a team is migrating incrementally.
Getting a project running
There are two documented setup paths, and the template path is the one to prefer because it wires up the project file for you.
dotnet new install "giraffe-template::*"Once the template is installed, the creation command is short enough to be worth keeping in a README of your own.
dotnet new giraffeThe manual path exists for cases where a template is not an option, such as adding Giraffe to an existing solution. Package Manager Console style commands install the framework package, and the ASP.NET Core shared framework reference is needed too if the project does not already carry it.
PM> Install-Package Giraffedotnet add package Microsoft.AspNetCore.App
dotnet add package GiraffeThe ordering of those two lines is not arbitrary, though the documentation presents them as alternatives rather than a sequence. A project that references the shared framework and Giraffe in the same pass is the expected shape, and the `Microsoft.AspNetCore.App` reference is the one that pulls in the host and the middleware stack Giraffe binds to.
One legacy note is still in the documentation. On .NET Core 2.1.4 the language has to be named explicitly with `dotnet new giraffe -lang F#`, which reflects the era when `dotnet new` guessed the language from the template name. Current versions of the .NET SDK infer F# from the template itself, so the flag is only relevant if you are pinned to an old toolchain.
Two versions of the startup snippet exist in the documentation, and the difference between them is the migration story for new services. The older version defines a `Startup` type with `ConfigureServices` and `Configure` members and hosts it through `Host.CreateDefaultBuilder`, which is the pattern described in Microsoft's own documentation about using `Startup` with the new minimal hosting model. The newer version drops the type entirely and calls `WebApplication.CreateBuilder` directly, with plain `configureServices` and `configureApp` functions. Both call the same `UseGiraffe` and `AddGiraffe` entry points, so the framework does not care which host shape you pick. Teams upgrading an existing service can keep the `Startup` class while migrating the host separately.
What the recent releases changed
Release 8.2.0, published in November 2025, is the release to read carefully because it carries breaking changes, and they are security changes. The new handlers added to improve security aspects include `safeRedirectTo`, `safeRedirectToExt`, and `validateCsrfTokenExt`. These deal with URL validation in `redirectTo` to prevent cross-site scripting, and with Cross-Site Request Forgery token validation helpers. In other words, a redirect helper that previously accepted any URL string now validates it, which is exactly the kind of change that breaks an application with an open redirect built into it on purpose.
The same release changes the XML serializer. The `Deserialize<'T>(xml: string)` method now uses a configuration to prevent XXE attacks, which means an XML document that relied on external entity resolution stops working. The release notes also record the removal of the AllowNullLiteral attribute from `Json.ISerializer` and `Xml.ISerializer`, driven by nullable reference types arriving in F# 9 with the release of .NET 9. When the feature is enabled with `<Nullable>enable</Nullable>`, users started running into problems that came down to `Json.ISerializer` carrying that attribute, so it was removed from both serializers and new automated tests were added to assert the resulting behavior.
Release 8.3.0, published in July 2026, is the additive release. It adds QUERY method support, promotes the router `*WithExtensions` functions to stable, adds `routeBind` support to `Giraffe.EndpointRouting`, renames the solution file from `.sln` to `.slnx`, and adds .NET 10 to the list of target frameworks under test while updating CI. Adding .NET 10 to the test matrix is the item with the longest reach, because it signals that the framework tracks the platform release cadence rather than lagging a version behind.
The project also introduced a Keep a Changelog document, which is a small quality-of-life change for anyone tracking breaking changes across upgrades. Combined with the release notes file at the root of the tree, there are two places to look before a version bump, and the release notes remain the authoritative list of behavioral changes.
Endpoint routing and the sample applications
Giraffe ships five sample applications inside the repository tree, and the set tells you something about where the framework is headed. The `samples` folder contains `EndpointRoutingApp`, `GlobalRateLimiting`, `NewtonsoftJson`, `RateLimiting`, and `ResponseCachingApp`. Three of those five are about ASP.NET Core infrastructure features rather than Giraffe features, which is the clearest available evidence that the design intent is coexistence.
`EndpointRoutingApp` exists because endpoint routing is the modern ASP.NET Core model and Giraffe has to work inside it rather than beside it. The 8.3.0 addition of `routeBind` support to `Giraffe.EndpointRouting` is a continuation of that work, and a documentation fix for named parameters on endpoint routing in the same release suggests the surface is still settling. `GlobalRateLimiting` and `RateLimiting` both exist because rate limiting is an ASP.NET Core middleware concern, and having a sample for the global variant alongside the per-application one is useful when you need to combine a Giraffe handler with a rate limiter that applies to the whole host.
`NewtonsoftJson` addresses the most common real world objection to an F# web stack, which is serializer choice. F# discriminated unions and option types do not map cleanly onto the default JSON contract, so teams frequently reach for `Newtonsoft.Json` or for a hand written DTO layer. The sample makes that path concrete. `ResponseCachingApp` covers the other common middleware add-on.
For a first project, the pragmatic sequence is to scaffold from the template, get a single route responding, then borrow from whichever sample matches the middleware you actually need. Reading `EndpointRoutingApp` first is worthwhile even if you do not plan to use endpoint routing, because it shows both handler styles coexisting in one project, which is the shape most production code ends up in after a migration.
Choosing Giraffe against the alternatives
The case for Giraffe is narrow and specific. It makes sense when the team writes F#, when the service already depends on ASP.NET Core for something, and when the routing and handler logic has grown awkward as controllers and filters. In that situation Giraffe removes ceremony without removing capability, because the host and middleware underneath are unchanged.
The case against is equally specific. If the team is not committed to functional style in the web layer, the framework offers little that a small amount of C# or another language would not offer with more community examples available. If you need a framework that runs standalone without a Microsoft host, Giraffe is disqualified by its own documentation. And if your application leans on the ASP.NET Core MVC experience specifically, controllers, model binding, filters, and view engines all arrive through C# conventions that F# does not improve.
The migration path matters more than the feature list in most evaluations. Because Giraffe is a middleware, you do not have to adopt it application-wide on day one. You can register it behind a single `UseGiraffe` call for one path prefix and leave existing controllers handling the rest of the routes, which lets a team prove the approach on a small surface first. That is only possible because of the complement-not-compete positioning, and it is the single most useful architectural fact about the framework.
What to check before committing: whether the F# language version in your build matches the nullable reference type change in 8.2, whether your redirect helpers rely on open redirect behavior that the new validation will reject, and whether the XML deserialization path you use depends on external entities. Those three are the upgrade cost. Everything else is ordinary framework adoption work.
Editorial conclusion
Giraffe earns its place by staying small and specific: one handler composition model, mounted inside a host that already solves everything else, licensed under Apache-2.0 and last pushed on 2026-09-28. Read the 8.2.0 release notes before your first upgrade, because the redirect and XML validation changes there are breaking and security motivated.
Frequently asked questions
Is Giraffe a replacement for ASP.NET Core?
No. Giraffe runs inside ASP.NET Core as middleware. The documentation states it is not designed to be a competing web product which can be run standalone like NancyFx or Suave, but rather a lean micro framework which aims to complement ASP.NET Core where it comes short for functional developers.
What is an HttpHandler in Giraffe?
An HttpHandler is the unit of request handling in a Giraffe application. Applications are composed of so called HttpHandler functions, which the project describes as a mixture of Suave's WebParts and ASP.NET Core's middleware, stitched together with operators such as >=> and >->.
How do I create a new Giraffe project?
Install the template with dotnet new install "giraffe-template::*" and then run dotnet new giraffe. The manual alternative is to add the Giraffe package plus a Microsoft.AspNetCore.App reference to an existing project.
What changed in Giraffe 8.2.0?
It shipped breaking security changes: new handlers named safeRedirectTo, safeRedirectToExt and validateCsrfTokenExt, URL validation in redirectTo to prevent cross-site scripting, CSRF token validation helpers, and XML Deserialize that now uses a configuration to prevent XXE attacks. The AllowNullLiteral attribute was also removed from the JSON and XML serializers.
Can Giraffe be used with ASP.NET Core endpoint routing?
Yes. The repository includes a sample named EndpointRoutingApp, and release 8.3.0 added routeBind support to Giraffe.EndpointRouting along with a documentation fix for named parameters on endpoint routing.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/giraffe-fsharp-giraffe)