Using Function Composition Instead of Options to Build Flexible APIs
By: David Barral
Tags: Function Composition, Functional Programming, Function, JavaScript

Photo by Nick Fewings on Unsplash

Some days ago I wrote about using functions and composition instead of hoarding props to define flexible React components. I would like to dig deeper into this concept outside the scope of React.

In this story we’ll make a refactoring exercise to get to a conclusion: functions and composition can help us build flexible APIs that are easy to understand, maintain and extend.

The big function approach

Let’s start coding a synthetic example: a data fetching operation. It allows us to use options to configure how the data is fetched and the response handled:

Define which HTTP verb will be used.

Prefix a base endpoint for relative paths.

Authorize requests with a bearer token.

Return the response as JSON or plain text.

Reject the fetch promise on server errors (the fetch API only rejects client errors, it’s up to the developer to ask if a request was ok or not based on its status code).

With these functions in place we can build different requests:

fetchData is big. It does all the stuff, and it’s all intertwined. Every new feature we want to support will add a new option and will make the code even messier.

Some refactoring

Let’s try to improve the function above extracting each individual responsibility to a separate function.

This new fetchData is simpler. We see how our newly extracted functions affect the request options and the response. However, there is still too much conditional stuff there. How should we test that? Each option in isollation? But what happens if there are interactions? Maybe it’s better to test all possible combinations but that would become unmanageable as the set of options grows.

Let’s try to move all those conditionals to the functions.

Look at fetchData . Do you see it now? Our functions either modify the request params or they modify the response. Individually, each of them can be seen as a fetch function that delegates in the original fetch: they are fetch decorators.

Enter decorators and composition

Let’s refactor the example one last time with our new acquired perspective.

With this approach we traded a fetchData driven by options for custom fetch methods built using basic verbs (a fetch with a predefined method) and decorators that offer the different behaviours we require (therefore using function composition). There is no conditional code here, there are no longer options. That’s test nirvana, if you ask me.

To ease the definition of decorated methods we can write our custom composition method: decorate .

You can see decorate as a pipe were the request options flow from left to right being updated, and the response flows from right to left being handled.

Simpler tools yet more powerful APIs

I hope that after the tedious refactoring session you can see how the humble function can help us build powerful and composable APIs. We switch from conditional code an runtime options to statically composing simple and focused functions.

The principles shown can be applied to anything. I’ve chosen a fetch based example for this story because I find pretty compeling the idea of building API clients using fetch verbs and decorators that provide different behaviours (authorization, CORS support, logging, etc.). Decorators that can later be freely combined with ad hoc ones. Everyone can code a decorator: at the end of the day, they are just functions.