What is a Fluent API?
An example - Making a Cake
Let us suppose we wish to write an API for baking a cake. We can call it CakeMaker. The sorts of steps we would like CakeMaker to allow might be:
- Gathering ingredients.
- Preparing them.
- Mixing everything together for the right amount of time.
- Baking the cake, at the appropriate temperature.
- Eating a slice.
- Perhaps finishing off with a well-earned snooze.
CakeMaker Rules
There are also some rules which govern how we make the cake:
- We need to do the steps in the right order. For example, we cannot prepare the ingredients before we have gathered them.
- Although, there might be some leeway to do some steps in a slightly different order. Or, do them more than once. For example, perhaps the ingredients will need to be mixed again, for a bit longer.
- We would like to give some more detail on how some of the steps will be carried out, such as how long the cake should be baked for and at what temperature.
- We want each cake 'instance' to be self-contained so, for example, the ingredients from one cake do not become mixed up with the ingredients for another.
The 'CakeMaker' Fluent API
A Fluent API allows us to address all these asks:
What is happening here is immediately clear. We can see that each of the steps is being carried out in the order we would expect. Furthermore, the additional information passed to each step is also easy to read.
Ensuring the correct order
What happens if we try to execute the steps in the wrong order? A Fluent API, if written to do so, can tell you when things are going wrong:
We can see above that the eat
method call has a red underline, indicating
an error. This is because this Fluent API has been written such that the first step has
to be gatherIngredients
.
Of course, all the type-safety we would expect for any method is still present. This is what
happens if we try to call the minutes
utility function, which expects a
number, with a string:
You would get this function parameter type safety without Fluent APIs. But this shows that, in tandem with standard, explicitly typed language features, a Fluent API can make it super clear exactly how your code is meant to be used.
Bending the rules
We considered, in the list of rules above, that we might want to do some steps in a different order, or even repeat some steps more than once. A Fluent API can allow this, too, whilst still preventing everything from turning into a free-for-all.
To illustrate, a single Fluent API can be written to work for all these:
It is clear to see that a Fluent API can be written to allow a lot of flexibility. There are, however, some limitations ...
Limitations
Due to the underlying Object-Oriented Programming constructs that Fluent APIs rely upon, the following limitations apply:
The Initiating Method cannot be repeated
The Initiating Method refers to the first method in the Fluent API method chain.
In our CakeMaker example, this would be the .gather(ingredients)
method.
It can only be called at the start, and it can only be called once.
The Executing Method cannot be repeated
The Executing Method is the final method in the Fluent API method chain.
The .eat()
method in our CakeMaker is an example of this.
It, too, can be run just the once, and only as the final step.
A workaround
What if you do want to repeat either the first or last steps more than once? One way to make this happen is to add extra placeholder methods at the start and end of the method chain.
For example:
- The Initiating Method could be called
init()
- The Executing Method could be called
exec()
... and then what were formerly the start and end methods can now be repeated (as long as the Fluent API is written to allow this):
Adding extra methods like this would usually require a lot of code changes, but the ability to easily re-order methods in Fluent API Generator makes the work comparatively trivial.
Method signatures must be unique
The methods used to build Fluent APIs should have unique signatures. They are, under the surface, object-oriented class methods and unique signatures for these is a standard requirement. This would usually be achieved by simply having different method names for each step, something you will likely want to do anyway for the majority of cases.
For Java and C#, repeating the same method name is allowed as long as the type(s) passed to each method are different.
The following would be allowed, for example. Although chop()
is repeated,
the first time it has no parameters (presumably because there is a default 'chop size'), whereas
the second time it takes a Size
parameter:
A further restriction for TypeScript
TypeScript does not allow repeated method names, even if the method
signatures are unique. The above example would not work for TypeScript. Instead, you
would have to use different method names, perhaps chop()
and chopSomeMore(Size.Fine)
.
Fluent API Generator will allow you to use the same method signature or name a second time. It is up to you to decide when this might cause issues with your chosen language.
Repeated step sequences are necessarily infinite
One or more steps can be looped. A set of steps can be looped within another set of steps. And, two sets of looped steps can overlap.
Here are some examples that illustrate this:
These examples are only the start; more complex arrangements are possible, allowing several loops to overlap, for example, or loops that are nested several levels deep.
However: it is not possible to limit the number of times a loop is repeated.
Yes, you could write code to throw an exception if a step is repeated, say, more than once. But, this would not give you the in-context red underline that tells you that only two steps are allowed, while you are coding.
To illustrate, this is not possible:
For the most part this will not be an issue. But, if you want to signify to your users that a step can only be repeated a certain number of times ... a Fluent API won't help you.
If you do need some control over how many times an operation is carried out, an option is to use non-repeating steps and a different step name for the second step:
Visualising looped steps
Fluent API Generator will give you a visual representation of which steps can be looped. If the Highlight Circular Chains option is checked then you will see permitted loops indicated:
Here we see two loops which overlap. There is more detail on this in the Help popup available in the Examples section of Fluent API Generator.
Aren't all these limitations a bit... limiting?
Yes! For these reasons, and others
Nevertheless, Fluent APIs can bring great clarity of intent for your users in the right
circumstances. They are found in many popular frameworks, as is
Try it out and see
Building a complex Fluent API from scratch, only to discover it is not suitable for your use case, can be a frustrating waste of time. Fluent API Generator makes it trivial to try out some examples, to see if a Fluent API will work for you.
The 'Pseudo-Fluent' API
A true Fluent API gives you control over the order in which methods are called, and how many times.
There is a lesser equivalent to this which gives you method chaining like a full Fluent API, but does not provide any additional guidance on ordering or looping.
Functions can be called in any order, repeatedly, or not at all:
There would be no red underlines indicating problems, and no way to know anything is amiss until you try to run the code... or, even worse, your users try to run the code.
For some scenarios, where order really doesn't matter, this is fine. The Pseudo-Fluent API still gives clarity as to what is being executed, and in what order.
For languages that lack static typing, such as JavaScript, the Pseudo-Fluent API is far more common. Large parts of jQuery, for example, use Pseudo-Fluent APIs liberally. This works well as it suits the super-fluid nature of DOM manipulation.
It is actually possible to write a full Fluent API in JavaScript. But, this requires using a more functional approach and is beyond the scope of this article.
Lastly, if building it from scratch, a Pseudo-Fluent API is comparatively simple to write.
Indeed, the journey to a full Fluent API begins with a Pseudo-Fluent API,
In many cases, a full Fluent API would be a better choice than a Pseudo-Fluent API, but the comparatively complex code required is a barrier.
Fluent API Generator changes that.