In this article, I will talk about the features and capabilities Midmare, and also share my experience in creating my own library.
The product I am working with is currently undergoing a migration process. Until it is over, we exist in parallel on two platforms and move away from legacy. I am doing a web application backend for a new platform. Some processes have to be constantly unified with other teams.
Taking these features into account, it was necessary to find a flexible and universal solution to rewrite the existing backend. This solution was the Midmare library.
JS is not typed, so development is much faster with it, albeit less stable. We were willing to take such risks as we planned to protect ourselves by writing enough tests.
At first, for a week and a half, I thought about the concept, went through different options. Until I came to the idea that it would be cool to use a functional approach so that it would be possible to insert any link in any chain of execution without consequences.
I came up with the idea to use middleware
Namely – to make a chain of intermediate processing functions that are executed one after the other and react to each other’s errors (errors, problems, data changes, etc.).
So I got a stack of middleware, which is similar in principle to Koa. This similarity ensures that the rest of the library is easy to use. But in Midmare there are significantly fewer methods, only 4. The Midmare library itself is minimalistic.
Beginning of work
Before installing Midmare, you need to download and install Node.js version 10 or higher. When creating a new project, you must also first create a package.json with the command npm init…
Installing Midmare is done via the command npm install midmare…
Below is a sample application initialization.
General application initialization on Midmare
Using routers (router-level middleware)
First and foremost, we’re talking about the impressive code decomposition capabilities. Intermediate functions (middleware) may not know anything about each other. They just take data, process it and release it further.
The Midmare library is ideal when you need to use the same routing system for different APIs – source and destination. That is, when you need to create a certain common processing node. But you can also create simpler applications.
Here are some examples to highlight the capabilities of Midmare.
Catching errors using an intermediate function. He’s an example of code decomposition
Simple intermediate function to close the HTTP session in case of route mismatch
An example of processing a command, in this case, sending data to Redis
The small library does its little job, but it doesn’t limit the options available to you in any way. In Midmare, you can connect virtually anything – without puzzling over how to build an architecture.
The key advantage of this library is complete isolation from any layers (HTTP, WS, etc.). If Koa is designed for an HTTP server, then Midmare does not create restrictions in this regard.
You can even just write a terminal utility. With Midmare, this is convenient since we can use parameters in “. As in the example below:
Using Midmare, you can go back to the same HTTP server. It will not be difficult to connect it. There is already a ready router for HTTP…
With this, you can also safely connect WebSockets. You just need to initialize the WebSockets client itself. And then it’s up to the most simple routing.
Example of Combining HTTP and WebSockets with Midmare
This versatility eliminates the need to think through different ways to solve the same problem in one program.
In fact, it is rather difficult to enumerate all the possibilities of implementation, because any idea is applicable to the library without restrictions.
I have not seen ready-made libraries of this format
That is, a complete analogue: without low-level bindings, whether it be WebSockets, HTTP, or, more generally, network communication or software work in the OS itself.
Midmare can do it all at the same time and literally in 5-6 lines of tuning.
And the idea behind it is quite simple – to build a chain of execution of functions using the next / send method to call. If nothing further needs to be called, the program will stop. Plus routing, minus HTTP – and we have Midmare.
Why use this library?
You can always write your own solution. Of course, in this case, your decomposition or the code itself may be lame (for example, you may not be able to wedge in between steps in the chain). If there are many such flaws, then a program written by hand can come to a bad ending in a year or two. Midmare avoids this.
Besides, there are too many filters in the world of programmers. Someone wants to write in TypeScript, someone in JS. I was looking for a solution to capture both options.
Now I try to actively test and refine Midmare
A big plus of a small library is that its creator actively supports it.
I test my library every day and, accordingly, quickly find pitfalls or flaws. With a small library, any problem can be fixed in no time.
Cyclic calls are the first of the fixed issues
Let’s take a look at the example below:
Let’s say we need to add a ‘/ send’ call to the .process (‘/ message’) step, but .process (‘/ send’) already contains a ‘/ message’ call. Then Step # 2 will call Step # 1. At the same time, in Step # 1, the call to Step # 2 will be triggered each time – and so on in a circle.
When you work on creating a library, you need to catch such problems, show them to users, explain what it is fraught with.
In this case, we will get the Maximum call stack size exceeded error and JS will crash in response to multiple recursive calls to the same function.
The solution to such a problem in this library: Keeping track history in one context. That is, each call to `ctx.send` or` app.send` has its own call history.
Since Midmare has a handle on this point, the library will throw an error if called in a loop.
There may be a situation in which a circular call is needed. For example, two intermediate functions handle the same data differently because there is a possibility of breaking out of the loop. That is, when one of the functions contains an if-else, which will work in the else and ignore the cyclic call.
In case the implementation requires cycling, Midmare has an option to set ignore errors via `ignoreCycleError: true`.
The library is waiting for its community
In principle, it makes no sense to add anything to such a pure library. It would be nice to add the ability to connect plugins, but with middleware this can be solved without new functionality.
If in a particular case some processing is missing, you can add it yourself to conditional 4 lines. As in the example with the function to close the HTTP session:
All additional processing can be moved to a separate repository. Or to distribute routers to repositories. Everything will be connected and processed the same way.
What the library really would do well for now is a community that would build its own tools from the library. Therefore, I invite the community to contribute to the development of the library. Join and test.
Author: Ivan Petushinsky, Senior Node.js Developer