Creating A Composable Web Server in Typescript

I suppose we should start where we must when discussing a Composable anything, which is “why should we care about composability?”

Have you ever heard the analogy that programming is like playing with Legos? How many times have you been in a large codebase and thought, “Wow! I can combine any of these things in any way I want to build new things!”

I think it’s fair to say there’s a disconnect between how composable we think our code is versus how composable it actually is.

So then what makes something composable?

Well the first thing I think of is the Closure property. The Closure property essentially says that given a function that accepts two arguments of the same type it will always return a value of the same type. Generically it would look something like this A -> A -> A. (For those interested in reading more about this, the most basic algebraic structure that upholds the Closure property is called a Magma).

Writing code that adheres to the Closure property means that we can combine pieces of code in any way we would like to build more complex things of the same type.

Creating a composable type

I’ve worked with many different web frameworks, but easily the best api I’ve used was from Suave.io which is written in F# and is remarkably composable. We’ll take it as our inspiration but will make a few different decisions to make it work nicer with Typescript and Node.

The base type for everything in Suave is called a WebPart. In fact everything in Suave essentially ties back into a WebPart because of the Closure property. To create a similar structure in Typescript we’ll define the following.

Pretty simple right? Because of the way Typescript handles interfaces and a concept known as “row polymorphism”, a WebPart is any object with a run property that is a function from A -> Promise<Maybe<A>>. As it turns out the A is generic so this type doesn’t really have anything to do with the web and could be used for all sorts of other things by simply providing a different A.

There are lots of good tutorials on Maybe if you aren’t familiar with it, but basically it’s a way of defining in the type system that we may or may not have a value. It’s similar to saying A | undefined except the type is defined elsewhere rather than being inline. My Typescript version is here

Specifying for the Web

It may still be unclear how we’ll use this structure, so here’s a teaser of some functions we’ll write to create WebParts.

The methodFilter function is quite simple. It returns a WebPart<HttpContext> that either succeeds or fails depending upon whether the Request’s method matches the provided string. And so we’re not always dealing with strings everywhere, I’ve added GET and POST helpers.

The key part here is that we’ve specified we want a WebPart<HttpContext>. By fixing the type variable to HttpContext we get to use all the properties of our WebPart type and specialize our run function to deal with HttpContext concerns.

Ways of composing

Now that we have a type to work with, how do we want to combine them? I can think of at least 2 ways in which I’d want to do that as a developer.

  1. With an and relationship ( appending one WebPart to the next )
  2. With an or relationship ( choosing the first matching WebPart from a list )

Appending one WebPart to another

Back to our Lego analogy, we want to be able to snap any block to any other. In our case we’ll have loads of things that can create a WebPart ( http method filters like we saw earlier, route filters, content writers, etc ) and we want to be able to compose those things in any way we see fit. The implementation for appending two WebPart types has some complexity to it, but I’ve documented our steps with types to make it a bit easier to understand. Ultimately the implementation doesn’t matter as much as the concept of combining two things to get another of the same type.

And now armed with append we’ll be able to write code like this:

So while that’s neat, our API is… lacking. Those nested calls to append are going to get really tedious when we start building more complex applications. If only there was some way to take a list of things and return a single thing…

Reduce to the rescue

That’s literally what reduce does!

This works OK in a pinch, but it would be nice to have this inline reduction stuff written within the library to work with any number of WebPart types without forcing us to write that same reduce function every time. But what happens if we receive an empty list?

It turns out that in addition to Closure, our WebPart with append has another property that we can leverage called Associativity which can be demonstrated with the following code.

Because app and ap2 are the same app regardless of whether we associate the first two elements first or the second two elements first, append is Associative. In Abstract Algebra that means that our WebPart type with the append function is a Semigroup.

Hmm.. that still doesn’t do anything for our empty use case though. We need another property known as Identity to handle our empty list use case.

empty simply uses our succeed helper from earlier to provide a base case where we default to the chain of WebParts as being successful. Said another way, empty can be appended to either side of an existing WebPart without affecting the WebPart. For some simple examples of the Identity property, you can think about adding 0 to any number, or multiplying 1. It’s also the same concept as concatenating an empty string to another string, or passing negative infinity to a max function. I could go on, but I think you get the idea.

Now that we have Closure, Associativity, and Identity we have what is known as a Monoid, and it’s just the structure we need to abstract reduce!

Our pipe function starts with our empty and simply calls append pairwise as we traverse through the array. Now with this function we can clean up those examples from earlier.

That results in a much nicer api, and we’ve also seen how we can abstract the concept of reducing an array by providing a Monoid instance for a type. I think that’s enough for now. Here’s what we’ve accomplished

Summary

  1. We’ve introduced the WebPart type that everything in our web server will use
  2. We’ve given it properties of Closure and Associativity with the append function. (Making it a Magma and a Semigroup)
  3. We’ve given it the Identity property with the empty function. (Making it a Monoid)
  4. Finally, we’ve used the fact that we have Closure, Associative, and Identity properties on the type to define what it means to combine 0 to many of them into one WebPart

We’ll build up the choose function in the next post which will “choose” the first matching WebPart from a list of WebPart. Here’s a teaser:

Like what you read? Give Reid Evans a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.