Well I've been far away these last months, quite busy at work also looking for something to share with the Dev.to community.
I have been playing around too much with .net lately for my personal projects as well as trying to finally figure it out so I can eventually add it to my I can do that sh*t
belt. So far some frontend js and node are in that belt. I'd like to extend it a little bit more.
I hope to share more of the experiences with writing UWP apps in C# vs Js in later posts but for now I'll take a look at Giraffe.
(you can skip the first two sections if you only want to see the code)
First of all F#?
Yeah I have tried dozens of times before to figure out how to actually do something meaningful in F#, but so far my attempts were kind of frustrated by me not understanding FP at all.
The fact that often when you look at FP resources you get to tend these mystical-hocus-pocus-magical
applications/buzz words where you only see these one liners very often and a ton of functions that seem to have depths within other functions and such didn't help either. Some discussions in internetland are quite authoritative on how you should do FP in the first place.
I learned from OOP so while I kind of find the use cases for small chunks of functions, I have never grasped totally the idea of getting these chunks together to do a big thing and it's not bad actually I'm just bad at grasping these FP resources because I didn't really had something to apply these things.
What's Giraffe?
Giraffe is somewhat a library/framework built on top of asp.net core for functional programmers, so yeah you have your kestrel server, access to common asp.net dependency injection, middleware and such. Like the author said in one of his blogs
Ideally as an F# developer I would like to replace the object oriented MVC framework with a functional equivalent, but keep the rest of ASP.NET Core's offering at the same time.
It was the idea when he began to work on it and he did it really well with the help of the community around Giraffe.
that being said...
Something to share
AngelMunoz / Giraffarig
Small Skeleton of a F# Web API with MongoDB and Giraffe
Giraffarig!
This is a F# Skeleton for some of my personal projects using .Net Core, MongoDB and Giraffe.
Things to do after cloning
- Don't forget to rename your
Giraffarig
references yo your project's name - Change your Enviromental Variables/Variables names in
Properties/launchSettings.json
and inside the code - Change your Database Name in
Db.fs
- Run
dotnet tool restore
- Run
dotnet paket restore
Disclaimer
I'm not good at F#. I think this language finally clicking on me with this sample API. I think I should be able to start digging deeper into F# for more use cases than just a simple Jwt Login now.
Found Something Wrong?
I do intend to learn F# more, but first of all I'm not much into mystic currying/partial application/composition (just about enough) so be gentle with me.
That being said, please feel free to raise an issue and point out bad patterns you might spot asโฆ
Now please take in mind that I'm no F# ninja not even close I kind of grasped the concepts that allowed me to do that skeleton by understanding other technologies like express(Js), asp.net core (C# version), Flask and others
so my Functional Fu is not top notch however I really like to highlight some of the concepts that allowed me to do something in F#.
The basic block for routes in Giraffe are these HttpHandler
functions
let routeHandler:HttpHandler =
fun (next : HttpFunc) (ctx : HttpContext) -> task {
return! Successful.OK "Hello World!" next ctx
}
now I have done stuff in express for a living for some time before and this function seemed familiar to me (except for lang + syntax y'know)
function (req, res, next) {
res.send('Hello World!')
}
Hah... intresting... after reading some of the Giraffe docs I kind of understood the deal, these functions were like express's middleware!
You can just use multiple small functions to pass along the request's pipeline (or however you may want to call it)
So I decided to pull in the MongoDB's .net driver and a login/signup as well as a user get route.
// Program.fs
let webApp =
choose [
GET >=>
choose [
route "/" >=> text "Hello world"
route "/api/users" >=> authorize >=> getUsers
]
POST >=>
choose [
route "/api/auth/login" >=> login
route "/api/auth/signup" >=> signup
]
setStatusCode 404 >=> text "Not Found" ]
so this was my router, two set of GET and POST routes
you can see that better in the repository but the deal here is kind of pattern you might spot
route "/" >=> text "Hello world"
that >=>
symbol is an intresting function, you might learn more about it here, but basically
The compose combinator combines two HttpHandler functions into one.
In that example, we use the function route, that takes one argument, then apply the result to the text function which takes a parameter too, in that way we can create a simple route which will deliver us a Hello World
you can see it better on the getUsers endpoint
route "/api/users" >=> authorize >=> getUsers
here we take a route, then an authorize function which will challenge the request and if successful, it will continue with the getUsers
function.
Now, before the getUsers function, let me share that using the mongo driver from F# is not hard at all
module Db
open MongoDB.Driver
open System
[<CLIMutable>]
type User =
{ Id: BsonObjectId;
Name: string;
LastName: string;
Role: string;
Email: string;
Password: string; }
[<CLIMutable>]
type EndpointResult<'T> = { Count: int64; List: List<'T>; }
let connectionString = Environment.GetEnvironmentVariable "MONGO_URL"
[<Literal>]
let DatabaseName = "fs_database_name"
let client = new MongoClient(connectionString)
let db = client.GetDatabase(DatabaseName)
let UserCollection = db.GetCollection<User> "fs_users"
quite straight forward right? we will use that UserCollection
later on to do our user queries
let getUsers:HttpHandler =
fun (next : HttpFunc) (ctx : HttpContext) -> task {
// a small helper function that gets limit/offset from query strings
let (limit, offset) = getPagination ctx
// no special filters, just give me that count
let count = UserCollection.CountDocuments(Builders.Filter.Empty)
let users =
UserCollection.Find(Builders.Filter.Empty)
.Skip(Nullable(offset))
.Limit(Nullable(limit))
.ToEnumerable()
// we have now our users let's get rid of sensitive information
|> Seq.map(fun user -> userToDTO(user))
|> Seq.toList
// return the users plus the next and context so it can be composed
return! Successful.OK ({ Count = count; List = users }) next ctx
}
Aaaand... just as that, boom you have a paginable endpoint! kind of similar on what you would do in express.
there is also Jwt authentication
in that repo so you might want to take a look to that too, jwt authentication is done with asp.net core
so that is a beneffit from Giraffe in the end it's just asp.net core!
Conclusions
With that sample up and running I can see myself using F# more often (Finally!) and I'm glad that I've used express before otherwise this could have been another failed attempt on F# for me :)
also I wanted to mention that I've tried to use asp.net core alone too with F#, but it was too messy and things didn't really worked well for me I kind of felt I was out of place when comparing it when using it with C#
Thanks for reading my none sense! I'm sorry if I wandered too much in some parts.
As always feel free to share feedback/comments down below!
Top comments (2)
Great post! Curious why you're using a NoSQL derivative like Mongo and not a relational data store?
Good question, since there are lots of sql variants for relational database access (fsprojects.github.io/SQLProvider/ or github.com/rspeele/Rezoom.SQL, and standard .net libraries), I thought perhaps it could be usefull for some people to know they aren't just limited to SQL, but NoSQL too