For the backend of our application we created a NodeJS server which serves as a platform.
The main goal of this application is to:
- Serve as an API for our admins to add users and homes to the database
- Make it possible for users to configure their home in the APP
- Make it possible for admins to add electronics/components to our backend
- Make it possible for users to view their home and connected components.
- Make it possible for user to get a component’s history
- Trigger (timed) actions for components
- Users get real-time updates from their home.
- Users can manipulate the components from the APP
Set up a google firebase account
- Login in firebase with your google account. If you don’t have one yet you can sign up here
- Go to console
- Add a new project (for testing purposes you can turn off google analytics)
- Go to Project Setting → Service Accounts
- Create a service account and click “Generate new private key”. Make sure to store this file in a save place and also add it to your .gitignore if your repo is public.
- Go back to the project main page and click the “</>” (web) symbol.
- Give your app a name and copy the firebaseConfig variable. Make sure to save this config in a file within your project.
The file “server.js” is the app’s entry-point.
We use this architecture to separate the responsibilities and make it easier to change/modify code.
- config: All app configurations go here. This means that all routes and our firebase config will be contained here.
- controllers: Where all the controllers are. All controllers have the same parameters e.g request and response.
- middleware: All middleware will go here.
- model: The model folder is obsolete but is helpful for knowing which attributes there are for certain models.
- repository: This folder will contain all the repositories that will request and or persist data to firebase
- service: Main business logic is here.
- utils: Some utility functions go here.
If you want to get the same pipeline like Java Spring or .NET Core then you will need to configure this yourself or use express (try to use the latest version or atleast 4.17.1 as old versions of express didn’t come with a basic request pipeline)
In server.js initialise express and add in an error handling middleware.
For this project which serves as a Proof of Concept we will enable cors for all requests. We also expect the request payload to always be JSON. Also setup all the routes and middlewares before listening for requests.
We need to tell express where to send the requests. We do this in app/config/routes.
For every route you can specify the middleware that you want to use. This makes it possible to have open endpoints and secured endpoints.
Also try to make all your controller methods asynchronous. This makes it possible to use the await keyword and improves code readability.
For our server we need to use 2 versions of firebase: Firebase SDK and the Firebase Admin SDK. Both need to be configured and separated from another.
Because we are building a home automation platform/service we will need to talk to electronic components like sensors and/or lights. For this we will use MQTT to make sure that our messages will get delivered to the home. We opted for a free online broker: emqx
Let’s look into how we set up the communication.
First of all we have 2 directions. Let’s call them channels that we will use. One channel will receive data from the home and the other will send actions that need to be performed.
Let’s create the receive side first.
First we make sure that we are connected to the broker and then we will subscribe to all the channels. We use the home id to differentiate the channels. We also subscribe with a Quality of Service of 1 which tells the broker that we want the guarantee to receive all messages in that channel.
After subscribing to all the channels we will setup a listener where we can receive the messages. We do this using the client.on(‘message’, callback) function.
We use websockets to send updates to our frontend application.
The reason why we use socket_io is that it enables us to create so-called rooms in our server so that we can target clients instead of broadcasting our messages.
A couple of things that are important:
- The subscriber needs to send the JWT token
- The socket_io version have to be the same in both frontend and backend
- The websocket needs to be run on a different port than the API.
In our code we have the API running on port 8081 and the websocket on port 3000.
Let’s look at how we get the token and authenticate the user to make sure that he subscribes to his home:
As you can see in the code we subscribe the user to his home and deny the connection if the supplied token is invalid. The same rules for the api server applies here as well. Register your callbacks/listeners and then start listening to the port.
Working with NodeJs is a completely new experience if you’re coming from a typed language like Java & .NET . A lot of times things don’t work because you used the wrong attribute name, forgot to await a promise (it happens),using the wrong library version,… . This was a refreshing experience and even when having some hardships with the used libraries, it was really fun.