Giraffe.OpenApi
0.1.0
dotnet add package Giraffe.OpenApi --version 0.1.0
NuGet\Install-Package Giraffe.OpenApi -Version 0.1.0
<PackageReference Include="Giraffe.OpenApi" Version="0.1.0" />
<PackageVersion Include="Giraffe.OpenApi" Version="0.1.0" />
<PackageReference Include="Giraffe.OpenApi" />
paket add Giraffe.OpenApi --version 0.1.0
#r "nuget: Giraffe.OpenApi, 0.1.0"
#:package Giraffe.OpenApi@0.1.0
#addin nuget:?package=Giraffe.OpenApi&version=0.1.0
#tool nuget:?package=Giraffe.OpenApi&version=0.1.0

Giraffe.OpenApi
An extension for the Giraffe Web Application framework with functionality to auto generate OpenApi documentation spec from code.
Table of Contents
About
Giraffe.OpenApi is a library that extends the Giraffe Web Application framework with functionality to auto generate OpenApi documentation spec from code. This means that you can define your API endpoints using Giraffe and generate OpenApi or Swagger documentation from it.
Inspired by the Oxpecker.OpenApi library, but adapted to work with Giraffe.
Getting Started
Add the Giraffe.OpenApi NuGet package to your project:
dotnet add package Giraffe.OpenApi
Two use cases:
open Giraffe
open Giraffe.EndpointRouting
open Giraffe.OpenApi
let endpoints = [
// addOpenApi supports passing detailed configuration
POST [
route "/product" (text "Product posted!")
|> addOpenApi (OpenApiConfig(
requestBody = RequestBody(typeof<Product>),
responseBodies = [| ResponseBody(typeof<string>) |],
configureOperation = (fun o -> o.OperationId <- "PostProduct"; o)
))
]
// addOpenApiSimple is a shortcut for simple cases
GET [
routef "/product/{%i}" (
fun id ->
forecases
|> Array.find (fun f -> f.Id = num)
|> json
)
|> configureEndpoint _.WithName("GetProduct")
|> addOpenApiSimple<int, Product>
]
]
Documentation
Integration
Since Giraffe.OpenApi works on top of Microsoft.AspNetCore.OpenApi and Scalar.AspNetCore packages, you need to do standard steps:
let configureApp (appBuilder: IApplicationBuilder) =
appBuilder
.UseRouting()
.UseGiraffe(endpoints)
.UseGiraffe(notFoundHandler)
let configureServices (services: IServiceCollection) =
services
.AddRouting()
.AddGiraffe()
.AddOpenApi("v1", fun options ->
// Register F# transformers
options.AddSchemaTransformer<FSharpOptionSchemaTransformer>() |> ignore
options.AddSchemaTransformer<DiscriminatedUnionSchemaTransformer>() |> ignore
)
|> ignore
[<EntryPoint>]
let main args =
let builder = WebApplication.CreateBuilder(args)
configureServices builder.Services
let app = builder.Build()
// Map OpenAPI endpoint
app.MapOpenApi() |> ignore
configureApp app
// Map Scalar API reference
app.MapScalarApiReference() |> ignore
app.Run()
0
To make endpoints discoverable by Scalar, you need to call one of the following functions: addOpenApi or addOpenApiSimple on the endpoint.
NOTE: you don't have to describe routing parameters when using those functions, they will be inferred from the route template automatically.
addOpenApi
This method is used to add OpenApi metadata to the endpoint. It accepts OpenApiConfig object with the following optional parameters:
type OpenApiConfig (?requestBody : RequestBody,
?responseBodies : ResponseBody seq,
?configureOperation : OpenApiOperation -> OpenApiOperationTransformerContext -> CancellationToken -> Task) =
// ...
Response body schema will be inferred from the types passed to requestBody and responseBodies parameters. Each ResponseBody object in sequence must have different status code.
configureOperation parameter is a function that allows you to do very low-level modifications the OpenApiOperation object using the new transformer pattern.
addOpenApiSimple
This method is a shortcut for simple cases. It accepts two generic type parameters—request and response—so the schema can be inferred from them.
let addOpenApiSimple<'Req, 'Res> = ...
Behavior and Nuances
- Request Type (
'Req):- If
'Reqisunit, the endpoint is treated as not requiring a request body (e.g., for GET endpoints). - If
'Reqis a tuple or a primitive type (e.g.,int,string), the endpoint is treated as not requiring a request body. These are typically used for path or query parameters, and the parameters are inferred from the route template. - If
'Reqis any other complex type (e.g., record, or class types), the endpoint is treated as requiring a request body. The schema is inferred from the type's fields.
- If
- Response Type (
'Res):- The response schema is always inferred from the type provided as
'Res. - If
'Resisunit, the endpoint is treated as not returning a response body.
- The response schema is always inferred from the type provided as
Important:
- You do not need to describe route or query parameters when using
addOpenApiSimple; they are inferred from the route template and the handler signature. - If you want path parameters to be named, use the
routeffunction with parameter labels in the route template (e.g.,routef "/users/%s:username/age/%i:age"). - Only use record or class types for request bodies. If you use a tuple or primitive as
'Req, it will be treated as path/query parameters, not as a body. - This behavior is designed with the idea that tuples and primitives are used for route parameters and records are used for complex request bodies.
Examples:
// No request body, returns a record
route "/hello" (json { Hello = "Hello from Giraffe" })
|> addOpenApiSimple<unit, FsharpMessage>
// Path parameters only, no request body
routef "/users/%s:username/age/%i:age" handler
|> addOpenApiSimple<string * int, string>
// Request body required (record type)
route "/message" handler
|> addOpenApiSimple<MyMessageRecord, string>
If your handler doesn't accept any input, you can pass unit as a request type (works for response as well).
configureEndpoint
The two methods above return Endpoint object, which can be further configured using configureEndpoint method provided by Giraffe. It accepts Endpoint object and returns the same object, so you can chain multiple calls.
let endpoints = [
GET [
route "/hello" (text "Hello, World!")
|> configureEndpoint _.WithName("HelloWorld")
|> configureEndpoint _.WithDescription("Simple hello world endpoint")
|> configureEndpoint _.WithSummary("Hello world")
|> addOpenApiSimple<unit, string>
]
]
License
This project is licensed under the MIT License - see the LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- FSharp.SystemTextJson (>= 1.4.36)
- Giraffe (>= 8.2.0)
- Microsoft.AspNetCore.OpenApi (>= 10.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Initial Version