Dependency Injection in F# Web APIs
Tags: dotnet, csharp, fsharp, api
Published: 2019-12-05 14:34:03

I've spent a lot of time thinking about the best way to manage dependencies in F# web APIs built on top of ASP.NET Core. As you might expect from a .NET library, it makes heavy use of object-oriented patterns. For example, everything seems to be based around the [built-in Inversion of Control (IoC) container](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection).

In many ways, IoC containers appear to be antithetical to the F# ethos. For one, they are a bit magical, which runs counter to the F# philosophy of being explicit. You also give up a lot of compile-time checking and have to rely on runtime failures for your dependency wire-up. Further, usually IoC containers are used with classes and interfaces, while many F# programmers [favor modules, functions, and algebraic types](https://dev.to/kspeakman/mere-functional-programming-in-f-do8).

On the other hand, containers do give you a good way to manage resource lifetimes. _Which things should be a singleton for the duration of the server, and which things should be created once per request?_

And really, since the container is built-in to ASP.NET Core, it's hard to avoid it completely. If you use the built-in configuration or logging, you'll likely need to use it. It's actually [impossible to create a `HttpClientFactory` without using the container](https://github.com/aspnet/Extensions/issues/1345)!

## A compromise

I've come up with a few strategies that allow me to be a good .NET citizen and get some of the benefits of containers, such as lifetime management, without giving up my desire to wire things up manually using partial application.

### Manual wire-up with partial application

When registering classes, it's common to register all the parameters to a class constructor in the IoC container, then let the container construct the class automatically via reflection.

The problem with this approach is that the construction of your class is now implicit. If you add a parameter to your class constructor and forget to register it, you won't get a compile error — it will just fail at runtime. I think this is one of the biggest drawbacks of containers.

Instead, I [use partial application](https://fsharpforfunandprofit.com/posts/dependency-injection-1/) to explicitly wire up my dependencies. Then I only register the top-level functions that will be injected into the controller.[^1]

Let's look at an example:

```fsharp
module Database =
    let readData (connectionString : string) (query : string) : Async<'a list> = ...

module Person =
    let getPerson (readData : string -> Async<'a list>) (personId : string) : Async<Person option> = ...
```

We have a `Database.readData` function that takes in a connection string and query and returns a list of results.

Then we have a `Person.getPerson` function that takes in a function that knows how to read data from the database and a `personId`, executes a query, and returns the result.

Notice that the `readData` parameter in `getPerson` is just `Database.readData` with the connection string already baked into it via partial application.

Here's what our container registration would like for this:

```fsharp
type GetPerson = string -> Async<Person option>

type Startup (configuration : IConfiguration) =
    member _.ConfigureServices(services : IServiceCollection) : unit =
        let readData = Database.readData configuration.["dbConnection"]    //partially apply the connection string
        let getPerson = Person.getPerson readData    //partially apply the readData function
        
        services
            .AddSingleton<GetPerson>(getPerson)
            .AddControllers()
        |> ignore
```

Yes, you can register plain functions in the container!

`GetPerson` is just a type alias to a function signature; we could have just as easily inlined it:

```fsharp
.AddSingleton<string -> Async<Person option>>(getPerson)
```

You'll note that we're doing all of the wiring manually via partial application[^2]. Then, we only register the top-level function, `getPerson`. The function `readData` becomes a supporting function for `getPerson`, but we have no need to register it because we won't inject it into our controller.

### Injecting records and discriminated unions

Injecting bare functions is simple and lightweight, but you do risk having functions with the same signature collide. For that reason, it's probably best to wrap the function in a record or [single-case discriminated union](https://fsharpforfunandprofit.com/posts/designing-with-types-single-case-dus/):

```fsharp
//Option 1: Record
type PersonControllerDependencies = {
    GetPerson : string -> Async<Person option>
}

//Option 2: Single-case DU
type GetPerson = GetPerson of (string -> Async<Person option>)
```

In the controller, you can inject the type into the constructor just like you would a class or interface:

```fsharp
[<ApiController>]
type PersonController(deps : PersonControllerDependencies) =
    inherit ControllerBase()

    [<HttpGet>]
    [<Route("people/{personId}")>]
    member _.Get(personId : string) =
        async {
            let! person = deps.GetPerson personId
            return OkObjectResult(person) :> IActionResult
        }
```

You may find it helpful to use destructuring:

```fsharp
let { GetPerson = getPerson } = deps
let! person = getPerson personId
```

This is especially true if you go the single-case union route because you can't just "dot" into it:

```fsharp
let (GetPerson getPerson) = GetPerson
let! person = getPerson personId
```

## What about Giraffe?

[`Giraffe`](https://github.com/giraffe-fsharp/Giraffe) is a functional web framework built on top of ASP.NET Core. Interestingly, `Giraffe` doesn't do anything to try to hide or work around the IoC container. In your handlers, you use:

```fsharp
let service = ctx.GetService<T>()
```

This is a service locator pattern. In some ways, this is worse than using stock MVC because at least with MVC you have a constructor to inject your dependencies into. For this reason, I like to add in [`Giraffe.GoodRead`](https://github.com/Zaid-Ajaj/Giraffe.GoodRead), which provides a slick way of injecting the dependencies into your request handler functions.

For contrast, here is what the request handler would look like with `Giraffe`:

```fsharp
type GetPersonDependencies = {
    GetPerson : string -> Async<Person option>
}

let getPersonHandler { GetPerson = getPerson } personId =
    fun next ctx ->
        task {
            let! person = getPerson personId |> Async.StartAsTask
            
            return! json person next ctx
        }

let webApp =
    choose [
        routef "/people/%s" (fun personId ->
            //inject the dependency into getPersonHandler
            Require.services<GetPersonDependencies> (fun getPersonDeps ->
                getPersonHandler getPersonDeps personId
            )
        )
    ]
```

## Final thoughts

I mentioned a few different approaches here. Let me give you a more concrete recommendation if you're still not sure where to start.

I recommend starting with stock MVC Web API first, especially for a simple app. Use partial application in `ConfigureServices` and inject a record of top-level functions. Records provide a nice grouping of related functions[^3] and provide an easy way to access the functions inside.

As your app grows and you become more comfortable, or if you anticipate needing request handler pipelines that compose easily, `Giraffe` with `GoodRead` is a good next step.

_This post is part of the [F# Advent Calendar 2019](https://sergeytihon.com/2019/11/05/f-advent-calendar-in-english-2019/)_

[^1]: It occurs to me that this strategy gives you less flexibility in managing lifetimes since you're not registering all the intermediate functions. I acknowledge this and am not sure how much of a problem this is in practice. It's also worth keeping in mind that you can manage lifetimes yourself outside of an IoC container. For example, you could have a singleton factory function that, when called, created a new resource every time.

[^2]: Well, most of it. The framework is still constructing the controllers themselves for us. If you really want to, you can create the controllers manually, too. [Here's a good example](https://github.com/dotnetjunkie/Missing-Core-DI-Extensions/blob/master/src/SampleApplication.PureDI/Startup.cs#L34) of how to do this in C#. I haven't played around with this much, so I'm not sure whether it's worthwhile.

[^3]: Yes, a record of functions is basically equivalent to an interface. Those will work fine too, especially if you pair them with [object expressions](https://fsharpforfunandprofit.com/posts/object-expressions/) to implement the interface, but I prefer records because it's easy to remember the syntax to declare and create them.