Simplify your functions adding “hooks” to your Promises
[EDIT 4/16/2018]: I received some feedback telling me that using Hooks could lead to a big code mess and should be clearly documented. It made me realize that my examples could be improved.
I added a “Where Hooks really shine” section below with hopefully a better use-case for when hooks can help you.
For my first post I am going to write about a simple technique to help you decouple pieces of functionalities of your code: hooks.
Hooks, or middleware, are functions executed right “before” or “after” particular methods. They make it very easy to plug-in/out functionalities and maintain the single responsibility principle of your methods.
You are probably familiar with the hooks of Express where functions are executed in sequence before hitting a route handler. Those hooks can prevent a route to be accessed or can add properties to a request. The hooks we will see here have the same purpose but instead of wrapping route handlers we are going to attach them to Promises.
To best understand the benefits of hooks, let’s see them through an example.
Adding middlewares to the Stripe API
Let’s take the Stripe Node.js API and add some hooks to its methods.
Imagine you have the following handler to process payments:
Wouldn’t it be nice if we were able to have a function executed right before a charge is about to occur or right after a successful charge? Of course we could add the functionality in our “processPayement()” method but then it’d be tightly coupled and hard to disable when needed.
Hooks to the rescue! To add “pre” & “post” hooks to the
charges.create() method we are going to use the promised-hooks package. I created this small library as I could not make the hooks library work with a chain of Promises.
Great, we now have our custom async service called right before a charge occurs. If the async service rejects the Promise, the charge won’t occur and the error will be caught in our error handling.
Let’s do a quick refactor to keep our Stripe API hooks in a separate file.
As you can see, adding “pre” and “post” functionalities to an API is easy and can greatly help the separation of concerns of your code.
Where hooks really shine
I received some feedback from people telling me that adding hooks like this could lead to a code mess, not seeing clearly how the program behaves. I do agree that the examples above, although they explain the concept, might not have been the best to show where hooks really shine.
Hooks are more useful when importing an API that we don’t control. Let’s continue with the example from above. Imagine that Team A has finished working on its “billing” npm package.
The code below is their small package to abstract the payment process. We can see that they haven’t forgotten to wrap their api with hooks to allow custom functionalities to be added.
Now it’s your turn. You are working on a new project that will accept payments using the package from Team A. After a few months in the project your are asked to block payments under 1 dollar and only allow payment by Card.
A good place to put your new business rules is in a middleware (the same way that Express lets us add middleware to prevent accessing a route).
Just like that you added two business rules to your application that will block payments below $1 or payment not made by Card. And you didn’t have to modify any of your code.
That’s it. I invite you to play with hooks and see if some of your codebase can benefit from them. Leave me your feedback in the comments below…
Thanks for reading!
Note to library authors: why not wrap your API with hooks to give your users the flexibility to run functions right before or after your methods?
It might be a very nice feature for them!