Create A Real Time Chat App With Golang, Angular 2, And Websockets

I’ve been hearing a lot about websockets lately and how they can accomplish real time communication between applications and servers.  They act as a compliment and possible alternative to RESTful APIs that have been around for significantly longer.  With websockets you can do real time messaging for things like chat, communication with IoT, gaming, and a whole lot of other things that need instant communication between clients and the server.

A while back I had played around with websockets and Node.js using a library called Socket.io, but since I’ve been really getting into Golang I wanted to explore websockets using the Go programming language.

We’re going to check out how to create a chat application where the client is an Angular 2 application and the server is a Golang application.

The Requirements

There are many moving pieces in this application so there are a few requirements that must be met in order to be successful.  The requirements are as follows:

The chat server which will orchestrate all the messages and clients will be written in the Go programming language.  The client front-end will be written in Angular 2 which has a dependency of the Node Package Manager (NPM) which ships with Node.js.

Building a Golang Chat Server

We’re going to start by developing the server component of our application which has a few external dependencies that must be downloaded prior.

From the Command Prompt (Windows) or Terminal (Mac and Linux), execute the following:

The websocket library was created by the same people who made the very popular Mux routing library.  We need a UUID library so we can assign each chat client a unique id value.

Create a new project at your $GOPATH which will represent our chat server.  My project will be found in the $GOPATH/src/github.com/nraboy/realtime-chat/main.go file.

Before going forward, it is important to note that I obtained a lot of the Golang code from other sources.  To avoid being an offender of plagiarism, I obtained portions of the code from Dinosaurs Code and the Gorilla websocket chat example.  However, I’ve included many of my own unique changes to the project as well.

The application will have three custom data structures:

The ClientManager will keep track of all the connected clients, clients that are trying to become registered, clients that have become destroyed and are waiting to be removed, and messages that are to be broadcasted to and from all connected clients.

Each Client has a unique id, a socket connection, and a message waiting to be sent.

To add complexity to the data being passed around, it will be in JSON format.  Instead of passing around a string of data which cannot easily be tracked we are passing around JSON data.  With JSON we can have meta and other useful things.  Each of our messages will contain information regarding who sent the message, who is receiving the message and the actual content of the message.

Let’s spin up a global ClientManager for our application to use:

The server will use three goroutines, one for managing the clients, one for reading websocket data, and one for writing websocket data.  The catch here is that the read and write goroutines will get a new instance for every client that connects.  All goroutines will run on a loop until they are no longer needed.

Starting with the server goroutine we have the following:

Every time the manager.register channel has data, the client will be added to the map of available clients managed by the client manager.  After adding the client, a JSON message is sent to all other clients, not including the one that just connected.

If a client disconnects for any reason, the manager.unregister channel will have data.  The channel data in the disconnected client will be closed and the client will be removed from the client manager.  A  message announcing the disappearance of a socket will be sent to all remaining connections.

If the manager.broadcast channel has data it means that we’re trying to send and receive messages.  We want to loop through each managed client sending the message to each of them.  If for some reason the channel is clogged or the message can’t be sent, we assume the client has disconnected and we remove them instead.

To save repeatative code, a manager.send method was created to loop through each of the clients:

How the data is sent, with conn.send will be explored later in this guide.

Now we can explore the goroutine for reading websocket data sent from the clients.  The point of this goroutine is to read the socket data and add it to the manager.broadcast for further orchestration.

If there was an error reading the websocket data it probably means the client has disconnected.  If that is the case we need to unregister the client from our server.

Remember the conn.send that we saw previously?  This is handled in the third goroutine for writing data:

If the c.send channel has data we try to send the message.  If for some reason the channel is not alright, we will send a disconnect message to the client.

So how do we start each of these goroutines?  The server goroutine will be started when we start our server and each of the other goroutines will start when someone connects.

For example, check out the main function:

We start the server on port 12345 and it has a single endpoint which is only accessable via a websocket connection.  This endpoint method called wsPage looks like the following:

The HTTP request is upgraded to a websocket request using the websocket library.  By adding a CheckOrigin we can accept requests from outside domains eliminating cross origin resource sharing (CORS) errors.

When a connection is made, a client is created and a unique id is generated.  This client is registered to the server as seen previously.  After client registration, the read and write goroutines are triggered.

At this point the Golang application can be run with the following:

You cannot test it in your web browser, but a websocket connection can be established at ws://localhost:12345/ws.

Building an Angular 2 Chat Client

Now we need to create a client facing application where we can send and receive the messages.  Assuming you’ve already installed the Angular 2 CLI, execute the following to create a fresh project:

This will be a single page application and what we hope to accomplish can be seen in the animated image found below.

Golang Angular 2 Chat Example

The JavaScript websocket management will happen from within an Angular 2 provider class.  Using the Angular 2 CLI, create a provider by executing the following:

The above command should create src/app/socket.service.ts and src/app/socket.service.spec.ts within your project.  The spec file is for unit testing, something we won’t explore in this particular example.  Open the src/app/socket.service.ts file and include the following TypeScript code:

This provider will be injectable and emit data based on certain events.  In the constructor method a websocket connection to the Golang application is established and three event listeners are created.  One event listener for each socket creation and destruction as well as a listener for when messages come in.

The send method will allow us to send messages to the Golang application and the close method will allow us to tell the Golang application that we are no longer connected.

The provider is created, but it cannot yet be used within each page of our application.  To do this we need to add it to the project’s @NgModule block found in the project’s src/app/app.module.ts file.  Open it and include the following:

Notice we’ve imported the provider and added it to the providers array of the @NgModule block.

Now we can focus on the page logic for our application.  Open the project’s src/app/app.component.ts file and include the following TypeScript code:

In the constructor method of the above AppComponent class we inject our provider and initialize our public variables that are bound to the UI.  It is never a good idea to load or subscribe to events within the constructor method so instead we use the ngOnInit method.

In the above method we are subscribing to the event listener we had created in the provider class.  In it we check to see what kind of event we found.  If the event is a message then we check to see if there was a sender and prepend it to the message.

You’ll probably notice that some messages are starting with a slash.  I’m using this to represent system messages which we’ll later bold.

Upon destruction, the close event is sent to the server and if the chatbox is sent, the message is sent to the server.

Before we look at the HTML, let’s add some CSS so it looks like a more legitimate chat application.  Open the project’s src/styles.css file and include the following:

Now let’s have a look at the HTML markup.  Open the project’s src/app/app.component.html file and include the following:

We simply loop through the messages array and display them on the screen.  Any message that starts with a slash will be bolded.  The form is bound to a public variable and when the send button is pressed, it will be sent to the Golang server.

Conclusion

You just saw how to create a websocket real time chat application using Golang and Angular 2.  While we aren’t storing a history of the chats in this particular example, the logic can be applied to much more complicated projects that include gaming, IoT, and plenty of other use cases.

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.

  • OSWALDO MONTAÑO

    hola, me gusto mucho tu artículo pero me gustaria saber si lo puedes subir a Github para visualizar mejor la estructura del proyecto y el código. Gracias de antemano.

  • Vidmantas Drasutis

    Use “npm install -g @angular/cli” for installing angular CLI

  • Santos Solorzano

    Include the following imports at the beginning of main.go! 🙂

    import (
    “encoding/json”
    “fmt”
    “net/http”
    “github.com/gorilla/websocket”
    “github.com/satori/go.uuid”
    )

  • Very nice and neat! Well done!