.NET Script APIs 🌞

5 min readApr 14


with F# and dotnet fsi 🌴

Saturn framework example

Minimal APIs have gained lots of traction recently in the .NET ecosystem, but what is a Minimal API anyways? Here is a quick definition:

… HTTP APIs with minimal dependencies. They are ideal for microservices and apps that want to include only the minimum files, features, and dependencies in ASP.NET Core. — [1]

// with a .csproj file so within a C# project
var app = WebApplication.Create(args);

app.MapGet("/", () => "Hello World!");


Based on the above definition, I propose a novel approach to minimal APIs for .NET in F#, taking advantage of F# interactive mode or REPL (read evaluate and print loop). Possibly something similar can also be achieved in C# with dotnet interactive, but F# is naturally equipped with a REPL as a dotnet tool: dotnet fsi, so I will stick with it as .fsx scripts also have great IDE support in Ionide, Rider, and VS. If you have ever programmed in Python or Node.js you have already used a REPL.

The usual “minimal APIs” in C# have a minor drawback though, a separate file for dependencies: .csproj file. Plus they cannot be executed interactively as there is no REPL. Other languages and frameworks like Deno in the “Nodejs” ecosystem, have taken a similar approach of small script APIs with direct in-file package reference.

<!-- .csproj file of a C# project -->
<Project Sdk="Microsoft.NET.Sdk.Web">
<!-- but when referencing actual libraries they will live here -->

Now let’s see how to create a minimal API in F# taking advantage of the interactive mode within a single .fsx script file.

Getting Started

First you will need a working .NET sdk installed on your machine 🤖, in case you are on Mac like me:

brew install dotnet

Now you can play with your dotnet interactive and learn how to exit the interactive session:

dotnet fsi

> 5;; // a value

val it: int = 5

> let x = 5;; // a value binding

val x: int = 5

> let sumPlusOne firstArg secondArg = firstArg + secondArg + 1;; // a function

val sumPlusOne: firstArg: int -> secondArg: int -> int

> #clear;; // clear screen

> #quit;; // exit the interactive section

Generate Aspnetcore Load Scripts

Thanks to theAngryByrd, who is also the author of the amazing IcedTask library, you can run THIS linked script to generate a folder containing your aspnetcore load scripts.

The #load directive will then be placed at the top of your single file .fsx app to load all the required assemblies to run aspnetcore in the interactive environment. If needed you can exclude this folder via .gitignore and just regenerate it each time you run your build pipeline, so you will not have to commit this same folder.

Saturn app

Now let’s create a test-folder for our playground 🚗

mkdir test-folder && cd test-folder

dotnet fsi generateAspnetcore.fsx

Now within this folder, you can create a single file (your whole app) named saturn.fsx and you can copy-paste the code from the gist at the top, and you can run it in interactive mode! 🍸 🍰

dotnet fsi saturn.fsx

Another way is also opening it with VsCode and Ionide.

Here is the result, after making an HTTP GET call to localhost:5000 🌝

running the app

To close your app a regular CTRL+C will send a signal to your process, and it will nicely show us the expected AspNetCore termination logs.

closing the app

HTTP in F#

Saturn is a web development framework written in F# which implements the server-side MVC pattern. Many of its components and concepts will seem familiar to those of us with experience in other web frameworks like Ruby on Rails or Python’s Django. It’s heavily inspired by Elixir’s Phoenix.

Built on top of the battle-tested ASP.NET Core foundation and the highly flexible, extendable model of Giraffe, Saturn provides high level abstractions, helpers and tools to enable high developer productivity, at the same time keeping high application performance provided by Kestrel and Giraffe. — [2]

Saturn and Falco are amazing web API frameworks, Saturn works with and on top of Giraffe, making it easier to configure and set up aspnetcore using computation expressions. It is also part of the SAFE full-stack framework which combines Saturn in the backend and Fable in the frontend to transpile F# to javascript (for another time!).

Giraffe is the de-facto amazing and most used framework, heavily inspired by Suave which introduced the 🐟 >=>operator, useful for composing HTTP async functions.

In Suave we program by chaining functions of type WebPart.

type WebPart = HttpContext -> Async<HttpContext option>

Suave uses the >=> operator to chain WebParts together. — [3]

Giraffe uses a very similar concept for the same operator.

Falco 🦅 app

Below a similar example but using Falco 🐦 framework, which is very slim and amazing as well!

Falco framework example

With similar results…

And yet, you could still use the older and amazing Suave framework which has a full HTTP server written in F# and not using Kestrel, but you would miss much of the benefits of running on AspNetCore and some interop if you are migrating from a C# solution, for example if you also use Microsoft DI and builders. In the latter case when using Suave you would have to arrange your own custom solution.

More on F# web frameworks