Swagger for F# tools
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



Swagger for F# project is destinated to produce libraries generating Swagger's documentation with REST frameworks like Giraffe and Suave.

This project needs feedbacks and is not really production ready.

Swagger for Giraffe


Waiting maturity I only published the library on my feed MyGet.


You can use NuGet to install the library:



In this project I propose a solution to generate a swagger for Giraffe. Issue https://github.com/giraffe-fsharp/Giraffe/issues/79 has label help wanted 😃 . Contributing direclty to Giraffe seems to be less reactive than creating my own project (see PR #218 )

My solution for Suave was effectively not really easy to use. Documentation and service implementation were too strongly coupled and the DSL was really verbose.

The good news is that we still have to declare our API routes the same way as before but to enable the route analysis we have to surround the app declaration with quotation marks.

With that in place we can decouple the app declaration from the analysis required to generate the swagger documentation. In other words this solution has the avantage to avoid corrupting your service implementation.

Getting started

Create the project

You can create your project with following steps.

dotnet new console --lang F#
dotnet add package SwaggerForFsharp.Giraffe --version 1.0.0-CI00004 --source https://www.myget.org/F/romcyber/api/v3/index.json

Open your .fsproj and edit your package references.

You should have something like:

<Project Sdk="Microsoft.NET.Sdk">


    <Compile Include="Program.fs" />

    <PackageReference Include="FSharp.Core" Version="4.5.*" />
    <PackageReference Include="Giraffe" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.1.1" />
    <PackageReference Include="SwaggerForFsharp.Giraffe" Version="1.0.0-CI00004" />
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.*" />
    <PackageReference Include="TaskBuilder.fs" Version="2.0.0" />



Edit Program.fs

module SwaggerGiraffeTesting.App

open System
open System.IO
open Microsoft.AspNetCore
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.AspNetCore.Http
open Microsoft.AspNetCore.Authentication.Cookies
open Microsoft.Extensions.Logging
open Microsoft.Extensions.DependencyInjection
open Giraffe
open SwaggerForFsharp.Giraffe
open SwaggerForFsharp.Giraffe.Common
open SwaggerForFsharp.Giraffe.Generator
open SwaggerForFsharp.Giraffe.Dsl

let errorHandler (ex : Exception) (logger : ILogger) =
    logger.LogError(EventId(), ex, "An unhandled exception has occurred while executing the request.")
    clearResponse >=> setStatusCode 500 >=> text ex.Message
let authScheme = CookieAuthenticationDefaults.AuthenticationScheme
let time() = System.DateTime.Now.ToString()
let bonjour (firstName, lastName) =
    let message = sprintf "%s %s, vous avez le bonjour de Giraffe !" lastName firstName
    text message

let httpFailWith message =
    setStatusCode 500 >=> text message

let docAddendums =
    fun (route:Analyzer.RouteInfos) (path:string,verb:HttpVerb,pathDef:PathDefinition) ->
        // routef params are automatically added to swagger, but you can customize their names like this 
        let changeParamName oldName newName (parameters:ParamDefinition list) =
            parameters |> Seq.find (fun p -> p.Name = oldName) |> fun p -> { p with Name = newName }
        match path,verb,pathDef with
        | _,_, def when def.OperationId = "say_hello_in_french" ->
            let firstname = def.Parameters |> changeParamName "arg0" "Firstname"
            let lastname = def.Parameters |> changeParamName "arg1" "Lastname"
            "/hello/{Firstname}/{Lastname}", verb, { def with Parameters = [firstname; lastname] }
        | _ -> path,verb,pathDef
let port = 5000

let docsConfig c = 
    let describeWith desc = 
        { desc
                Title="Sample 1"
                Description="Create a swagger with Giraffe"
    { c with 
        Description = describeWith
        Host = sprintf "localhost:%d" port
        DocumentationAddendums = docAddendums

let webApp =
        ( choose [
              GET >=>
                 choose [
                      route  "/"           >=> text "index" 
                      route  "/ping"       >=> text "pong"
                      // Swagger operation id can be defined like this or with DocumentationAddendums
                      operationId "say_hello_in_french" ==> 
                          routef "/hello/%s/%s" bonjour
              RequestErrors.notFound (text "Not Found") ]
       ) |> withConfig docsConfig

// ---------------------------------
// Main
// ---------------------------------

let cookieAuth (o : CookieAuthenticationOptions) =
        o.Cookie.HttpOnly     <- true
        o.Cookie.SecurePolicy <- CookieSecurePolicy.SameAsRequest
        o.SlidingExpiration   <- true
        o.ExpireTimeSpan      <- TimeSpan.FromDays 7.0

let configureApp (app : IApplicationBuilder) =
       .UseGiraffe webApp

let configureServices (services : IServiceCollection) =
        .AddCookie(cookieAuth)   |> ignore
    services.AddDataProtection() |> ignore
let configureLogging (loggerBuilder : ILoggingBuilder) =
    loggerBuilder.AddFilter(fun lvl -> lvl.Equals LogLevel.Error)
                 .AddDebug() |> ignore

let main _ =
    let contentRoot = Directory.GetCurrentDirectory()
    let webRoot     = Path.Combine(contentRoot, "WebRoot")
    let url = sprintf "http://+:%d" port
        .Configure(Action<IApplicationBuilder> configureApp)

Build and run

Run with

dotnet build
dotnet run

Go to url http://localhost:5000/swaggerui/index.html

How does it work ?

I introduced the documents function that takes two arguments:

  1. the quotation expression containing webservice implementation.
  2. a DocumentationConfig argument.

This function does the analysis of your quotation to generate Swagger documentation.

DocumentationConfig contains the following properties:

  • MethodCallRules: allow you to provide custom functions to enrich DSL and / or quotation analysis.
  • DocumentationAddendums: allow you to add more informations to the documentation without introducing service implementation modification.

I introduced ==> operator that gives the possibility to add decorations in routes implementations.


There are 2 solutions to add documentation for a route.

See example

operationId "send_a_car" ==>
	consumes tcar ==>
		produces typeof<Car> ==>
			route "/car2" >=> submitCar

using DocumentationAddendums

route "/car" >=> submitCar

let docAddendums =
    fun (route:Analyzer.RouteInfos) (path:string,verb:HttpVerb,pathDef:PathDefinition) ->
        match path,verb,pathDef with
        | "/car", HttpVerb.Post,def ->
            let ndef = 
                (def.AddConsume "model" "application/json" Body typeof<Car>)
                    .AddResponse 200 "application/json" "A car" typeof<Car>
            path, verb, ndef

Next steps


In futur, SwaggerUi could be a submodule of the repository (if you like and accept this PR 😄 ).

Quotations and Giraffe

Some features could be missing and some quotations could be difficult to parse. For the moment, analyzer works with most basics default httphandlers.

I only implemented:

  • GET
  • POST
  • PUT
  • route
  • routeCi
  • routef
  • setStatusCode
  • text
  • json
  • choose
  • subRouteCi
  • subRoute

You can build and run SwaggerSample/Program.fs and go to http://localhost:5000/swaggerui/



Next step will consist to add genericity and implement a version for Suave.io