Introducing R3lab: A Node.js web server library inspired from React.js
First of all, this is not your typical “Introducing X library”, it’s more like a “what if we mix some ideas and try something new”. My intention with R3lab is about experiencing with the Node.js and trying to organise a server-side app in another way. For me, because I’m mostly working on frontend and had some experience with JavaScript, I thought learning Node.js would be easy, but the challenges you will face while building back end are completely different than the ones you face while using JavaScript on the front end, so this is also an opportunity for me to keep in touch with the backend side.
Well, I know how that sounds, React.js is a frontend library, let’s just look at how large scalable server-side apps are done in Node.js. And because it’s one of the most popular frameworks, let’s have a look at Express.js.
What is Express.js?
Well, I am glad you asked. I think there are 4 reasons why Express.js is so cool to use:
- It’s modular and there are a ton of modules build for Express.js
- Middlewares
- It offers in-built routing solutions
- Factory. Everything is wrapped up in an object and to start the server you only have to .listen() to it
Let’s just have a look at Middlewares because, in my opinion they are the most awesome and simple thing in express. For those who are not familiar, just consult the docs:
Middleware functions are functions that have access to the request object (req), the response object (res), and the next function in the application’s request-response cycle. The next function is a function in the Express router which, when invoked, executes the middleware succeeding the current middleware.
Basically, in Express you can chain as many callbacks as you want to one route. This is nice and useful because you can modularize your logic as far as you want. Take for example when you have to check for a login credential do some validations or / and add some CORS headers. The result will look something like this:
The chart is not 100% accurate because, well, remember that I mentioned that there are modules built for Express.js? Take body-praser for example. When using it with Express you would do something like this:
But if you have a look on body-praser git repo they export this function:
So bodyParser is basically just another middleware function, Q.E.D.
Now let’s say that a POST to /ping must only have boolean types. Otherwize a 400 error should be returned. You could do all this in the route function, but remember, everything in Express.js is a middleware. By separating concerns we can do all the stuff in one place and handle errors in another place.
Here we check the request body and if we don’t like what we got, we throw an error that will be handled in the next() middleware. With this example we covered most of the express.js functionality.
What is React.js?
Now let’s switch sides and get to a JavaScript library used for building user interfaces. Let’s do the same and see a few key points:
- Declarative views. It uses components that let you split the UI into multiple independent pieces.
- Each component can have it own state and lifecycles.
- It handles events similar to DOM events, but in its own wrapper, Synthetic Events, for normalizing each behavior.
Let’s just see how a React.js component looks like (and that’s the only front-end piece of code I’ll include, I promise)
Lifecycles
First thing you might notice is that it starts with a constructor, then the component does mounts, it renders something to the user, and after Timer is not used anymore, it will unmount, right? Well for that you might know that React.js has 3 stages of lifecycles: mounting, updating and unmounting. In our example at mounting, the state of the component is initialized with { seconds: 0 } in constructor, it renders and displays to the user “Seconds: 0”. So at his point mounting is done, componentDidMount() method kicks in and Timer object is given the interval callback that will be executed at each 1 second. After 1 second, Timer.tick() method is called so the update stage starts. Because that special setState() method that is provided by React.js is called, after the state was changed, the component renders again displaying “Seconds: 1”. It keeps that flow for… ever? Well unless unmounting is triggered, yes. Usually a component unmounts when the user leaves or refreshes the page or the component is not used anymore. You might have a look at this diagram to understand the flow and you will also find this example right here on react docs. I’ll not get more into it but I’ll try to make an analogy with how those might be used on a server-side app.
First, we are not mounting or unmounting anything on backend so we’ll just have a single flow for now. We will consider a constructor to initialize anything from any primitive / constants we might need to a database connection object. Then we will have a before() method to do authentication or any validations for the data we receive, and because a request can have multiple methods like GET, POST, etc, we will also need a beforeMethod (like beforeGET, beforePOST, etc) and we’ll see later why. All the logic can stay in the method that the user requested. For a GET /ping our Ping component will call Ping.get(). Remember we had a db connection opened? Let’s just delete it and call Ping.destroy() for this. Oh, you’ve got an error? No problem, it was caught and Ping.onError(err) was called and you can handle it there and maybe send a message to the user so he knows that the server crash landed on a 500 (or whatever error message you decide it’s appropriate).
Is this C++? I knew that JavaScript is garbage collected. Why do we have destroy()?
Let me present you Memory Leaks! It’s a thing in Node.js. You can read more on this article about it, or I can just extract a point from it for you:
When the Garbage Collector runs, it pauses your application entirely until it finishes its work. so you need to minimize its work by taking care of your objects’ references.
Enough talk, let’s just get the Express.js ping example and migrate it to r3lab-server.
And that’s it. R3lab-server is designed to be flexible but also help users to better organise the code. In the example above, when any kind of request is made to /ping, it will construct a PingRoute Object, call before() method first, then beforeMETHOD(), method() and at last destroy() where it deletes all references it has on its context.
Code reusability
Another important idea is to reuse as much logic as you can. Let’s take the example where you have a protected route where data is returned only if the user making the request is authenticated. In express this can be done simply by having a middleware that checks received credentials.
In our case you can overwrite the Route class and add your own logic into any method:
Error management
Express provides basic error handling for untreated errors.
But let’s say we have 2 completely different routes:
- /publicData – exposes some resources to public, no auth required, used by everyone
- /privateDevData – exposes resources only to authenticated users that have technical knowledge of what’s happening on the server side and want some detailed messages if something goes wrong.
The solution above is shorter than r3lab-server counterpart, but if you want to implement the scenario I’ve presented, the level of complexity will increase. Wrapping just everything that can fail in try – catch blocks might not be so funny. What if both /publicData and /privateDevData have error methods of their own?
Well, that’s exactly what you will find in r3lab-server. Oh, you want to handle errors in /publicData/whatever/100other/endpoints? Remember what we’ve discussed in the Code Reusability section. You can have a ErrorHandlingRoute that overrides the onError method and you’ll only have all logic in one place.
R3lab-server provides ServerError that is just extending the basic Error object provided in JavaScript by adding the status property to help decide the http status. For example, if something fails in the process of validating the payload received, it means we should respond with a 400 BAD REQUEST.
Conclusions
I’m planning to add more functionalities to this library because Node.js is good for building the server-side of real-time web applications that do not require much computation or lots of db queries. That’s why it’s still being used more and more even if its initial release was more than 10 years ago. Of course it has its own challenges, but I think that investing in it is a good choice. If you have any suggestions please reach out to me. Also I’m happy if you can contribute to the git repo.
TL,DR: You can just head to the project repo for description and examples.