Using MiniProfiler with FSharp

What is MiniProfiler, and why should you care?

According to it’s website:

“A simple but effective mini-profiler for .NET and Ruby


Well, “performance is a feature”, and MiniProfiler helps a lot with that: Profile you code, see calls to your storage (including SQL, and there are others)…

The interesting part for F# developers is that is runs on .NET Core too, therefore should work with Giraffe and Saturn as well. In fact, it does work!


MiniProfiler traces can help with debugging on your machine too. You see exactly what did happen, not just guessing…

Also, if you save the traces in production, you can get the current profile’s idfrom code, and save that info in your exception log. So profiling info is immediately available during troubleshooting!

But there are other performance and crash monitoring tools?

Yes, but this was working for me for years, and stores all data locally. Not tied to any third-party services. You won’t be leaking sensitive personal, financial, business etc information accidentally!


Getting it running is pretty easy despite that there is no official support for Giraffe / Saturn / SAFE-Stack app.

  • Add the required nuget package
  • Configure profiling
  • Add the required javascript to you index page

Ok, there were some gotchas, but it does work! Let’s get to the details!

First, add nuget dependency

Just install MiniProfiler.AspNetCore.Mvc package.

This package contains Asp Net Core bits, and pulls in the core MiniProfiler package as dependency.

Configure profiling

This was straight forward, just followed the samples in the docs. OK, syntax is slightly different, but was pretty easy to figure out:

At this point if you run your app, you should start seeing MiniProfiler’s trace ids in the response headers:

So far so good, but you still can’t see it’s UI popup, let’s do that!

Add MiniProfiler’s javascript to the page

Or not

Because the profiler itself works, and you can check your result at http://localhost:8080/mini-profiler-resources/results-index. That’s the default path, but it is configurable.

Result list

But you lose two things:

  • Seeing the results right away, in the same browser tab, without clicking around
  • Client timings

Client timings?

MiniProfiler reports back how long the request took from the client’s point of view. To look up DNS, send the request, network latency, parse everything and render the DOM! This tells a lot more than just server processing time!

MiniProfiler’s client timings

Add that javascript UI!

So, MiniProfiler has a javascript bit that renders the UI. The package has a tag-helper for razor, so one option is to change your index page to razor, and you are good to go.

Or look around in the source, and find the part that does the real job! It helps that I used MiniProfiler for years, so I knew that there was a non-tag-helper method to render the includes, called RenderInclude.

All that needs is an HttpContext, and returns a string. So you can use it in Giraffe’s view engine, just pass in the HttpContext as a parameter, and you are good to go!

And you are done:

Profiling ajax-calls

You can profile “ajax” (is that still called that way, or it’s me being ancient?) calls as well. How does it work? Actually the includes monkey-patch the global XHR, fetch objects to enable profiling. Monkey-patching global objects is not something you should, but since those APIs weren’t built with intercepting and profiling in mind, I guess it’s OK, it works, and had no problems with it for several years.

One thing to note: If you want the profiler to catch your ajax / XHR / fetch request, add the includes before your main JS bundle. If the result doesn’t show up, just open the console, and send and empty fetch() or $.get()to your server. Previous results should show up.

What about other type of apps?


Console .NET Core apps are supported by MiniProfiler, here are the docs.

Freya and WebSharper

Honestly? Don’t know for sure. But as far as I know, both do run on top of Asp.NET Core and Kestrel, so should work.


Suave is it’s own webserver, there is no HttpContext, so you would have to do all the plumbing to properly get it running and profiling your Http requests.

Conclusions / next steps

Yeyy I have profiling for my F# SAFE-app, I’m happy with that!

In production

If you are using it in production, don’t forget to set up authorization, otherwise anybody can see it! Optionally, you can conditionally include the RenderIncludespart, if you don’t want client timing information.

Also, by default it stores results in an in-memory cache, so change storage to something else (there are storage providers available as NuGet packages).

Is something missing?

Depends on :)

I’m using the SAFE-Stack for SPA application, so pretty much my only server rendered view is the index, in other cases, YMMV, but should work…

Currently, there is no support for view render profiling, doesn’t know about Saturn’s controllers, subControllers, plugs, so those things won’t show up in the traces. But you can create wrappers for those things!

Also, depends on the whole MVC package, that means more dlls to deploy.

Still, for me, the benefits outweigh those issues and megabytes :)

Next steps

I guess this is enough for an intro blog post. Didn’t show how to profile your code in depth, database calls, set up authorization, filter out requests you don’t want to profile… Maybe next time.

The code

Code is up at GitHub. The sample is created with the SAFE-Stack template, the second commit shows the steps to get the profiling working.