The basics: send and receive messages

Last month, my friend Anthony working in a big, corporate company told me

“We love Slack. However, we don’t want our messages to be stored in a cloud. We would like to start building a self-hosted clone in the near future: how would you do it?”

Here’s my answer.

For the front-end, I had many choices. After taking a close look at some of them (basically, the most popular, namely React, Angular 2, Cycle and Vue), I’ve chosen Vue. Why?

It’s straightforward. It has a CLI that sets up a great development environment with hot-reload working out of the box.

It’s elegant. Once we take a look at the file structure later, we see how clearly it is organized. Way better than JSX `return` values or Angular 2 `template` strings with no code highlights. `.vue` files are neat and beautiful.

For the back-end, I’ve been facing a big bunch of solutions. I’ve chosen Kuzzle.io, here’s why.

It’s self-hostable and Open Source. I love the cloud, except when it is raining too much. And what recently happened with Parse was a hell of a storm for many of us. We’ll avoid embarrassing situations by hosting our own back-end.

It’s straightforward. I find that being able to have it working by just typing `docker-composer up` is quite a sunny solution. And yeah, I love the sun.

It has an awesome SDK. Many ready-to-use backend solutions provide front-end SDKs, but Kuzzle’s one is so offline-first... I love to see that it just works even when the connection goes up and down.

Kuzzle.io is a ready-to-use and extendable back-end application developed in Montpellier, France. It has recently reached the Beta release and it is getting growing consensus on the web development community.

Front-end environment setup

Prerequisites. To follow this How-to you should be familiar with the Node.js environment and npm, modern Javascript coding in general and the Docker ecosystem.

Vue.js kick-start

So, what’s happened here? The Vue CLI has initialized a new project for us with all the necessary files and directories. Then we npm installed the dependencies specified in the generated package.json. Lastly, we npm run our development server.

Now, if you go to http://localhost:8080/ you can see an auto-generated “Hello World” project.

Hands on Code!

Ok, let’s do it. Go to src/App.vue and take a look at the top-level tags:

<template>...</template>

<script>...</script>

<style>...</style>

Guess what goes in each of them?

template contains your markup;

script contains your interaction logic;

style contains your component-specific stylesheet.

This is how every Vue component looks like. App is simply the root component, which is instantiated and attached to the DOM in src/main.js (our entry point). This will be the only time the DOM is directly manipulated.

If you like discovering things on your own take a look of what’s inside each tag but, for now, you can just delete their content and leave them empty, we’ll overwrite everything once we’ll have created…

…Our First Component

The most basic thing we use instant messaging for is… Writing stuff! Our first component will be called src/components/InputBar.vue and guess what it will look like?

What has happened here? We have told the template tag that we want an input-bar element. To enable Vue to find it, we have imported and declared it as a component into the script tag. The result? You should see an input field on your page.

Where are my messages?

Ok, this isn’t so groundbreaking. After all, a Slack that doesn’t display messages is a bit… pointless? We need to add some more bits here.

This component is quite like the previous one but with two slight differences:

The {{message.content}} statement in the template. This is data binding expression. It means “put here the content of the variable message.content”.

The props keyword in the script tag. Props are local variables that contain data that is passed from parent components. We’ll use them a little bit later but, basically, what we are saying here is “The message variable comes from parent”.

The v-for loop directive in the template. It’s just a for loop and it goes like “display as many message components here as many elements you find in the messages array. Also, pass each element as the message prop to each component”.

The data method in the script tag. We use this to locally declare some temporary dummy messages to display in the list. We’ll replace this with something more interesting later…

Altogether now! Make src/App.vue look like the following (new stuff is written in bold)

Stores, where you put the data you want Components to display. A store is a plain javascript object.

Services, where you put the logic that fetches or changes the data. A service is a plain javascript object.

We use services to fetch the data that we put into stores. Components read the data from the stores and display it. When data change in the stores, the components are instantly updated thanks to Vue’s reactive data-binding mechanism.

we declare the onEnter() method to call sendMessage() on the store passing newMessage to it (remember that newMessage always contains the current value of the input).

Yo, you can now play with your pretty input field and see your messages appear.

Notice. Passing the whole store as a prop to a component is a very bad practice since it couples the component to the structure of the store. Only individual methods should be passed to the components, but this would have added a little bit of complexity in the code and we wanted to focus on our task at this stage of the How-to.

“I Feel Alone…”

That’s what my friend Anthony told me when I showed him my front end. He was right.

It’s totally lame to build an instant messaging tool to chat on your own. What if we had a back-end that could connect many instances of your new front-end and make them receive each other’s messages? It would be rad. Well, with Kuzzle.io this is quite easy.

Back-end Environment Setup

Notice. Be sure you have the ports 7511 and 7512 free on your host before you go on. Also, this section assumes you have docker and docker-compose installed on your computer. See here for installation details.

Create a new empty folder (let’s call it kuzzle) and put thisdocker-compose.yml file into it (don’t change the name of the file).

Now my friend Anthony is very happy! He has a back-end with real-time pub/sub, storage and document search. Exactly what he needs for his instant messaging application. Keep this terminal alive to let Kuzzle run.

From now on we’ll talk to Kuzzle on localhost, but if your are on Windows or Mac, your Kuzzle URL will be the one of your docker machine.

In Kuzzle, collections are like arrays of documents (or, if you prefer, like SQL tables). Notice that we referenced the ‘messages’ collection even if we never explicitly created it. This is because Kuzzle will create it for us once we put anything into it.

createDocument(message) tells Kuzzle to create a new document (containing the message) in the collection.

In Kuzzle, documents are like JSON object (or, if you prefer, like SQL rows).

This code just enabled us to send our messages to Kuzzle, which persists them as documents. Sweet. But how do we receive other user’s messages?

Receive Notifications from Kuzzle

One awesome thing about Kuzzle is that every time something happens on a document (e.g. a new document is created) a real-time notification is triggered. To receive our messages, we just need to subscribe to the notifications about the documents contained in the ‘messages’ collection.

Here, we subscribed to all the notifications being triggered within the ‘messages’ collection. Let’s see it in depth:

dataCollectionFactory(‘messages’) you know this guy;

subscribe() takes a filter (we leave it empty) an options object (we don’t need any) and a callback that is called with the notification from Kuzzle. If the notification contains no error, all we want to do is to take its content and store it in our messages array.

Note. All we do here is composing a new object with the attributes of _source (see the ES6 spread operator) plus the _id of the message.

Like that, every new message coming from Kuzzle will be instantly displayed by the Messages component.

Note. We do not push our message in the messages array within the sendMessage function anymore. That’s because we get notified by Kuzzle about our own messages (see the subscribeToSelf option for the subscribe method).

Last but not least, we need to start the subscription on the application startup so, go to src/App.vue and change it as follows:

Now, go ahead and open many windows at http://localhost:8080 and see how messages are broadcasted among all the instances.

Summing up, with one docker-compose command and 15 lines of JS code we just set up back-end data persistence and real-time notifications.

High fives, dude, my friend Anthony is happy!

A Little Exercise

You may have noticed that, even if you are able to receive messages from all your users, there’s noavatars. So you won’t know who wrote what. Not very convenient for an instant messaging app.

Try to Do It Yourself and see the solution on the next episode, starring… Properly Log Users In and Out!

[Bonus] Always Be Stylish

Notice. If you don’t give a damn about styles, this section is not for you.

So far we’ve been having fun playing with the basics of Vue and Kuzzle but my friend Anthony made me notice that the real Slack is more beautiful. He is right. But, know what? We can fix this. If you want to wait for Part 2 with a gorgeous UI, you can grab a ready-made Slack-like stylesheet we provided for you.

Webpack goes glamour

Don’t be shy and drop the ready-made stylesheet into src/style/global.css.

Now, we’ll have to teach Webpack how to handle CSS. Add this line on top of build/webpack.base.conf.js

What?

It’s all set to enable Webpack being able to load stylesheets. This enables us to use require or import directives in our JS code to import CSS files. Yes. What we have just done is tell Webpack “Hey, when I import a CSS file, get it and add a link tag for that file in the head of the page”.

Let’s add the following line at the top of src/main.js to load the stylesheets: