This post will be outlining (more of a brain dump) of my experience with using F# to build our latest free DNS tool ddnss.net.

See our launch blog post

TLDR;

Site: https://ddnss.net/

F# Packages: Fable, Feliz (+UseElmish), Thoth.Json, FsToolkit, Glutinum Iconify

Other packages: Vite, Tailwind, DnsClient.Net, Serilog, LiteDB

Template: https://github.com/Dzoukr/SAFEr.Template

Tooling: Rider + cli

This isn’t a full list but covers 99% of what is powering the site. On Github, the project is 98.6% F# code!

The full journey

Why build this?

Here at Aspire Web we manage hosting and domains for most of our customers along with being technical consultants for digital agencies and some MSP’s (managed services providers).
This requires us to look up domains and DNS quite a lot and over the last few years the existing tools we used were not as effective. In large part this is because of CloudFlare blocking the ANY query which was a quick way to get an overview of a domain.

I started using different tools but either they only did part of what I wanted or in the case of websites were covered in ads (slowing down the browser in some cases!) or just slow to get anything.

The solution

I wanted a really simple tool that would allow the Aspire Web team and customers to easily check their domain. It should prominently show their nameservers, base A records, MX and TXT (spf/dmarc).

It should be quick, clean and with a decent UX (hey I’m no designer).

Initially I was thinking this could even be just a single page.

I’d been building small CLI’s, data processing console apps in F# for the last ~12 months and while I did try out the SAFE stack in the past, it was just to experiment.

Based on this knowledge I wanted to use F# for a few reasons:

  1. Backend and frontend could both be in the same language (I already switch between enough different languages)
  2. Shared types between client / server
  3. Higher assurance of the code
  4. Low maintenance, deploy and use. I didn’t want to have to fix it every week
  5. Feliz/React should provide a good UX (tested them in the past)

The build

I’ll start with saying this was, umm a lot harder and time consuming than I thought. That’s normal for most projects to be honest but in this case there were a few additional factors.

1. Using Feliz, I’m not a React dev
While I’d done a little bit of react, we deal with A LOT of WordPress sites which 99% of is PHP or plain old html/css.
This is meant I had to learn some react ways of doing things, to then work out how to do it in F#/Feliz… this wasn’t always that easy. If your coming from a React/Vue world this won’t be as bad.
Having to convert Html to Feliz (even with the converters) was also an extra step that slowed things down earlier on.

2. Good UX takes time and iteration
Getting the UX to a place where I would be happy to recommend the tool to partner business and possibly more techy customers is quite iterative. Normally we work with different partners to help with this so doing it myself was a bit more involved.

Okay back to the build…

The POC

To prove my ideas I wanted to build something simple and ugly. I looked at a few options to help stream the DNS as it was resolved to the client and decided on Server Sent Events (SSE) over websockets or other similar tech.

I needed a design and had been wanting to try v0 from Vercel. It worked quite well, so I used it to create a general design of the site and iterated with it a bit to to get a good starting point.

Building It

For the react side we were using useElmish, which is a component/page based Elm way of working. In hindsight we could have gone full Elmish but if the project grows this is likely still the better choice.

Now we get to the boring part, getting the right data, processing it and iterating the front end to handle it. I used DnsClient from MichaCo and it handled the lower level DNS client. I did have to refactor massively after testing some domains as I needed the authoritive DNS chain for some situations. Unfortunately this does slow down the lookups but on the positive we get better quality data (NS and TTLs)

The big thing for me was to really make sure the user knew what was going on, were we still waiting on data was it loading something, etc. This meant a combination of client side models that understood the current state and the server sending the right events to make that work.

While this was more involved than I was hoping for I’m quite confident I covered majority of the situations and states the which I couldn’t say for a lot of other projects as they change and grow.

I did hit an issue where a library was different on server vs client as the Fable implementation wasn’t the same, within a week of reporting the issue to Fable it was fixed and released! That was amazing and a big thanks to Maxime!

Near the end I just needed to add in some simple logs/data collection so added in Serilog and LiteDB for some data collection. Not sure I’ll stick with LiteDB but for now it’s fine.

I don’t think the SAFEr stack template includes tests, so I also created some here using the base SAFE template for examples.

Deployment was a bit rushed and I knew I needed more time to really automate this fully so currently it’s using some existing infrastructure while I get time to create a good way of doing this.

Alternatives?

I’m sure there were a lot of ways to build this and typically I do reach for SSR, either basic server templates or a mix with Livewire/HTMX/AlpineJS.

I’m super happy with the choice to go with Fable + Feliz as I got a better UX than I think I’d have got with SSR + JS along with the stability of the client state when using MVU (Model View Update, or the ELM way).

Challenges

Feliz/React:

Learning technology is always time consuming and takes a bit of trial and error so plan for this. The biggest issue is that most of the React ecosystem will need thin wrappers to map them. This took time to figure out that unless I wanted to spend the time building the wrapper to just testing a component maybe I should just go for an easier/existing solution. I think Maxime is working on something here that might help in the future?

Also some wrappers were based around using full Elmish and not useElmish, which again was more time than I had available to work out a solution. Maybe I’m missing something here that is simple, let me know.

Some documentation/blogs were also a bit old and not relevant anymore when searching on this. Fable extensions being a weird one to figure out and understand what is/not included in the core. I found a blog on using Elmish but the subscriptions were changed and had to do it with useEffect and without the MVU model.

As basic feature I wanted was to copy to the clipboard, now in basic JS you can do the unsafe thing but of course you cannot do that in F#, you’re forced to handle bad outcomes. Had to use a promise and piecing it together was hours (skill issue I know) vs minutes using React (my implementation was safer though).

Libraries

This is a problem with all projects but based on some (possibly old) suggestions I went down the wrong path for JSON and I had to try a few different JSON libraries before settling on the Thoth.Json ones as it worked perfectly between both server and client.

Be willing to remove things that aren’t working and add in new ones. I removed Giraffe after having it in for 80% of the project, there was no easy way to add in asp.net rate limiting for minimal API’s so I went raw .net for this and it was fine. I did check OxPecker and it looked promising but after looking at the source, it was also appeared to not support this feature.

The SAFEr stack I used from Roman used DaisyUI (which he made a wrapper for) out of the box which is a nice library but most of what I did was custom so I removed it and made the components I needed.

Required knowledge:

The stack covers everything from build, server, client, styling. This is not just a client React app that you connect to CloudFlare or Vercel and deploy “serverless”.
This means you need to know A LOT and while I dislike the term, you need to be more of a “Full stack” developer. Being in the game for many years I’ve had to go through learning a lot of this along the way but for someone new this could be a bit daunting. Bringing a junior onto this project would have been quite hard.

Hopefully this doesn’t deter anyone as I think anyone can learn and it’s great to have a bit of cross over between client/server even if your focus is only on one of them.

Benefits

Single language

Normally on web projects I’m constantly switching between server (PHP/C#) and client (HTML/CSS/JS) code. Having the one language was a bigger benefit than I expected in keeping flow. It’s hard to explain until you get a chance to do this and I guess it’s why people are moving towards NextJS and the like as you can stay in one language.

Elmish/MVU

The MVU model is quite amazing, I don’t think I’m using it always perfectly and it does required more thought but once you have it working it really is great. The fact you can easily do Elm in F# sites is a massive win, especially with useElmish as a lighter alternative.

Shared Types

Having the ability to share types between client/server was amazing here, break it in one spot and then fix everywhere.

As I got more comfortable with this, I did refactor multiple times and I found I was more willingly to than I normally would in other languages knowing the compiler would yell at me when I did something wrong.

Assurance / Certainty

This is really massive. It’s hard to overstate what this does for a project. Using DU’s along with the strong types means I’m much less likely to make a mistake or forget to refactor something correctly, especially as a project grows.

While it does take time to fix and make everything work, it made me design types/data flow better along with a massive amount of trust that it would work. I’ve been mostly working in a Laravel project along side this and it is WORLDS better in any assurance it will work or that a change somewhere else won’t break seemingly unrelated parts.

I didn’t make many tests and while I need to do more, the compiler forces me to cover cases I would have other wise missed.

Finishing up

I learnt a lot and had good fun building this even through the frustrations along the way.

Would I use the same stack?
For this project yes, it’s a bit more data heavy, requires a good UX and I wanted something low maintenance.

As I went on I kept getting more ideas for this tool and I’ll certainly look forward to adding these in without the fear of regression bugs. I even have some ideas on how to monetize it in the future which was never the plan but would ensure it’s long term viability.

I hear about people not learning F# or other niche languages because of the job market but without us building software in F# there will never be more jobs. I hope this can encourage others to also try building their ideas, SAAS or tools in F#.
As a community we need to try and make F# at least AN option to choose when building vs it being perceived as a bad one.

Obviously this was more about the tech and journey but if you do have a need for the tool please use it or have suggestions, let me know.

Massive thanks goes out to the F#/Dotnet community who’ve built and maintain some of the packages used. Here’s a list of some of them:
https://github.com/MangelMaxime – Fable / Thoth / Glutinum
https://github.com/Zaid-Ajaj – Feliz + Elmish Book
https://github.com/Dzoukr – Template + example projects
https://github.com/MichaCo – DnsClient

If you got this far, thanks for reading my blabbing, it was good to get these details out of my head.

I’d love for you to share this with someone who might be interested.

Leave a Reply

Your email address will not be published. Required fields are marked *

one + two =