REST API with MongoDB and F# on .NET Core
I have just a couple years in functional programming, just a couple weeks in F# and no background or experience with .NET, so don’t take this as a reference from an expert, it’s just me documenting what I’ve learned so far.
You can start by building a very raw-vanilla-bare-bones HTTP server using the HttpListener from .NET, but is very low-level in terms of having few abstractions, like: you have to write on stream buffers to output something.
There is ASP.NET Core, but is is very object-oriented, it’s constructions aren’t functional and hey, we’re using F# for a reason, right?
Freya seams to be an awesome purely functional high-level way to go, but few maintainers and months without updates.
Then there is Giraffe, the survival of my biased criteria above, it’s high-level, functional-first and actively maintained!
You can start by using it’s dotnet-new template, but I like a greenfield so let’s get started from scratch:
λ mkdir Todos
λ cd Todos
λ dotnet new console -lang F#
We’re starting using the console template, it’s the simplest I’ve found, it will put just a minimal .fsproj file. You can run it to make sure everything is fine:
λ dotnet run
Hello World from F#!
Let’s add our dependencies. Giraffe runs on top of ASP.NET Core and we’ll be using the C#’s MongoDB driver, but don’t worry, it’s awesome for F# too.
λ dotnet add package Microsoft.AspNetCore.App
λ dotnet add package Giraffe
λ dotnet add package MongoDB.Driver
Their are all coming from NuGet.
Let’s change our Hello World to print on a Request:
To start our development server:
λ dotnet run
You’ll see a message telling you that it’s running on http://localhost:5000 head over to this URL on your favorite web browser and you should be seeing: Hello World from F#!
We have a running server! Now what?
We’re going to make this simple
Todos have a text telling what to-do and a boolean flag telling if it’s already done, despite a unique identifier, of course:
Framing our CRUD endpoints
Despite serving “Hello World” texts, of couse, Giraffe can delegate HTTP endpoints to function handlers defined as
HttpFunc -> HttpContext -> HttpFuncResult:
We also need to update our
Program.fs to include this handlers to our routes:
Start the server with
dotnet run and try it!
Before we put our hands on MongoDB, we could make a proof-of-concept using and very simple in-memory representation of what would be a Todo repository and how to operate our CRUD functions on it.
Also, it’s a way to make sure our domain isn’t too tied to any database vendor, we could replace MongoDB by any other type of storage just by re-implementing the function signatures to the new database.
Todo.fs with the following types:
It isn’t doing so much, but think on this like OO interfaces. The big deal here is that we’re not going to reinvent the wheel and instead use the awesome Dependency Injection constructs that ASP.NET Core already provides.
For example, our HTTP GET endpoint will look like:
We’re relying on the
TodoFind “interface” and for the endpoint, the implementation doesn’t matter, it could be in-memory, MongoDB or any other, it only knows that it receives a
TodoCriteria and returns a
Todo (which then Giraffe auto-magically parses to JSON through the
Yes, we’re doing Dependency Inversion with Functions!
Let’s implement the C and R parts of the CRUD in-memory with the help of a
You probably noticed that
find here is slight different from
TodoFind it is
Hashtable -> TodoCriteria -> Todo. That is because we need to share the same
Hashtable along with other functions, but no worry and thanks to Currying we can give a
find and it returns a perfect fine
TodoFind. We can inject it like a Singleton through
You got it, right? We are defining our
TodoFind with the “hashtabled”
find function from
TodoInMemory. Let’s implement the
Then let’s inject it to our services, but don’t forget we have to share the
Hashtable so we’ll be finding and saving on the save place in-memory:
We can make things better here. Note that
IServiceCollection got an
AddGiraffe method? No, Giraffe isn’t built-in on .NET, it used the power of Type Extensions and we can do this to for
We aren’t avoiding a call to
AddSingleton to every function signature, but at least it’s well placed on the same module, is very clear and easy to reason about. We can use
AddTodoInMemory just like
Now we should update
TodoHttp to retrieve and call our functions, we already saw how GET looks like, here is the full file with the POST modification:
We’re already able to save and find some Todos!
Remember, it’s all in-memory on that mutable Hashtable, if we restart the server everything is gone.
First our final type
TodoDelete for Delete, we’re going to use
TodoSave for Create and Update:
Not much difference from in-memory, we going to replace the
Hashtable by a
IMongoCollection and refactor our save to know when to insert or update, also implement
And, as expected,
TodoHttp has no changes to Create and Read, let’s just implement Update and Delete:
And we replace services, from
AddTodoMongoDb. Of course, we’ll need a MongoDB client connection and a MongoDB database:
I’m assuming you have a running MongoDB server somewhere and we’re getting the connection string from the environment variables, so before you
dotnet run make sure o have this variables or set/export one. In my case I have MongoDB on localhost, so:
That is it! We have a functional-first, decoupled HTTP Rest API with F# and MongoDB. As said before, I’m new to all of this, so I’d love to hear some feedback from experienced folks and if this is new to you too, don’t mind to comment your questions, let’s figure it out together.