.NET Script APIs 🌞

jkone27
5 min readApr 14, 2023

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]

Based on the above definition, a novel approach to minimal APIs for .NET can be achieved with F#, taking advantage of its interactive mode or REPL (read evaluate and print loop).

What does it look like in C#?

/*

here is the directory structure.. more on this later

MinimalWebApi
│ appsettings.json
│ Program.cs
│ WebApplication.csproj

*/

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

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

app.Run();

C# with dotnet interactive can now probably achieve something similar by installing some extra global packages, but F# is naturally equipped with a REPL as a dotnet tool: dotnet fsi. And is awesome.

F# interactive programming in .fsx scripts has great IDE support in Ionide, Rider, and VS. If you have ever programmed in Python or Node.js you have already used a REPL.

C# Project File?

MinimalWebApi
│ appsettings.json
│ Program.cs
│ WebApplication.csproj

The usual “minimal APIs” in C# have a minor drawback though, a separate file for dependencies: .csproj file.

This is not a big deal, but when looking for dependencies, you do “need to know” how it works, and look for this file specifically.

<!-- .csproj file of a C# project -->
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<!-- plus when referencing actual libraries they will live here -->
</PropertyGroup>
</Project>

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

Note: 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.

Getting Started with F#

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 Load Scripts for F# Interactive

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.

dotnet fsi generateAspnetcore.fsx

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 generated folder using a .gitignore and regenerate it each time you run your build pipeline, so you will not have to commit to this same folder.

Saturn app

Saturn framework example — same example as before

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 run this code, 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 is a similar example but using Falco 🐦 framework, which is very slim and unique 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

--

--