Building a Real-Time Collaboration App with Go and WebSocket

Table of Contents

  1. Introduction
  2. Prerequisites - Go Installation - WebSocket
  3. Setting Up the Project
  4. Building the Server - Creating a WebSocket Server - Handling WebSocket Connections - Broadcasting Messages

  5. Building the Client - Creating a WebSocket Connection - Sending and Receiving Messages
  6. Testing the App
  7. Conclusion

Introduction

In this tutorial, we will build a real-time collaboration app using Go and WebSocket. The app will allow multiple users to connect to a server and collaborate in real-time by sending messages to each other. By the end of this tutorial, you will have a basic understanding of how to set up a WebSocket server and client using Go, and how to handle real-time data communication.

Prerequisites

Before you begin, ensure that you have the following prerequisites:

  • Basic knowledge of Go programming language
  • Go installed on your machine
  • Familiarity with the concept of WebSockets

Go Installation

To install Go, follow these steps:

  1. Visit the official Go website at https://golang.org.
  2. Download the installer suitable for your operating system.
  3. Run the installer and follow the instructions to complete the installation.

  4. Verify the installation by opening a terminal or command prompt and running the following command:

     go version
    

    If the installation was successful, it should display the installed Go version.

WebSocket

WebSockets provide a bi-directional communication channel between a server and client over a single, long-lived connection. It allows real-time data transfer and is widely used for building real-time applications such as chat systems, collaborative editing tools, and more. WebSocket communication is based on the WebSocket protocol, which is built on top of the HTTP protocol.

Setting Up the Project

To begin, create a new directory for your project. Open a terminal or command prompt and navigate to the desired location. Once there, execute the following command to create a new folder:

mkdir real-time-collab-app

Navigate into the project folder:

cd real-time-collab-app

Now, initialize a Go module for your project:

go mod init github.com/your-username/real-time-collab-app

This command will create a go.mod file that will track your project’s dependencies.

Building the Server

Let’s start by building the server-side logic using Go. Create a new file called server.go in your project folder.

Creating a WebSocket Server

In server.go, add the following code:

package main

import (
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

var clients = make(map[*websocket.Conn]bool)
var broadcast = make(chan Message)

// Message struct for incoming and outgoing messages.
type Message struct {
	Username string `json:"username"`
	Content  string `json:"content"`
}

func main() {
	// Configure WebSocket route
	http.HandleFunc("/ws", handleConnections)

	// Start listening for incoming chat messages
	go handleMessages()

	// Start the server on localhost:8000
	log.Println("Server started on :8000")
	err := http.ListenAndServe(":8000", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func handleConnections(w http.ResponseWriter, r *http.Request) {
	// Upgrade initial GET request to WebSocket
	upgrader := websocket.Upgrader{}
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Fatal(err)
	}
	// Close the connection on exit
	defer conn.Close()

	// Register new client
	clients[conn] = true

	for {
		var msg Message
		// Read in new message
		err := conn.ReadJSON(&msg)
		if err != nil {
			log.Printf("error: %v", err)
			delete(clients, conn)
			break
		}
		// Send the newly received message to the broadcast channel
		broadcast <- msg
	}
}

In this code, we import the necessary packages such as log, net/http, and github.com/gorilla/websocket. We define two global variables; clients, a map to store all connected clients, and broadcast, a channel to broadcast messages to all connected clients.

The Message struct defines the structure of the incoming and outgoing messages. The main function sets up the WebSocket route, starts a goroutine to handle incoming messages, and starts the server on localhost:8000.

The handleConnections function is responsible for upgrading the initial HTTP request to a WebSocket connection. It registers the new client in the clients map and listens for incoming messages. Once a message is received, it is sent to the broadcast channel.

Handling WebSocket Connections

Next, add the following code to server.go:

func handleMessages() {
	for {
		// Get the next message from the broadcast channel
		msg := <-broadcast

		// Send the message to all connected clients
		for client := range clients {
			err := client.WriteJSON(msg)
			if err != nil {
				log.Printf("error: %v", err)
				client.Close()
				delete(clients, client)
			}
		}
	}
}

The handleMessages function listens for messages on the broadcast channel. Whenever a new message is received, it iterates over all connected clients and sends the message using the WriteJSON method. If any error occurs while sending the message, the client is closed and removed from the clients map.

Broadcasting Messages

Finally, let’s add a WebSocket endpoint in server.go to serve the HTML page for the client. You’ll also need to import the html/template package at the top of the file:

import (
	// ...

	"html/template"
)

// HomeHandler serves the home HTML page
func HomeHandler(w http.ResponseWriter, r *http.Request) {
	homeTemplate := template.Must(template.ParseFiles("index.html"))
	homeTemplate.Execute(w, nil)
}

func main() {
	// ...

	// Configure other HTTP routes
	http.HandleFunc("/", HomeHandler)

	// ...
}

Here, we define the HomeHandler function to serve the index.html file using the template package. In the main function, we configure the root / route to use this handler.

Building the Client

Now that we have the server logic in place, let’s create the client-side JavaScript code to establish a WebSocket connection and handle messages.

Create a new file called index.html in your project folder with the following contents:

<!DOCTYPE html>
<html>

<head>
    <title>Real-Time Collaboration</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <h1>Real-Time Collaboration</h1>
        <div>
            <input v-model="message" @keyup.enter="sendMessage" placeholder="Enter a message">
        </div>
        <ul>
            <li v-for="msg in messages">
                <strong>{{ msg.username }}</strong>: {{ msg.content }}
            </li>
        </ul>
    </div>

    <script>
        const webSocket = new WebSocket("ws://localhost:8000/ws");

        const app = new Vue({
            el: '#app',
            data: {
                message: "",
                messages: [],
            },
            methods: {
                sendMessage: function () {
                    webSocket.send(JSON.stringify({
                        username: "User",
                        content: this.message,
                    }));

                    this.message = "";
                },
            },
        });

        webSocket.onmessage = function (event) {
            const msg = JSON.parse(event.data);
            app.messages.push(msg);
        };
    </script>
</body>

</html>

In this HTML file, we import the Vue.js framework and set up a basic UI for the collaboration app. It includes an input field to enter messages and a list to display received messages.

We create a WebSocket connection using the WebSocket object and define a Vue instance to handle sending and receiving messages. When the user enters a message, it is sent to the server as a JSON string. The received messages are parsed and added to the messages array, which will automatically update the UI.

Testing the App

To test the app, open two separate terminals or command prompts. In the first one, navigate to the project folder and start the server by running:

go run server.go

You should see the server started message in the console.

In the second terminal, navigate to the project folder and open the HTML file in a web browser:

open index.html

This will open the collaboration app in your default browser. Enter a message in the input field and press Enter. You should see the message appear in the list, indicating successful communication between the client and server.

To test real-time collaboration, open the same HTML file in another browser window or tab. Enter messages in one window and observe them appearing in real-time on the other window.

Congratulations! You have built a real-time collaboration app using Go and WebSockets.

Conclusion

In this tutorial, you learned how to build a real-time collaboration app using Go and WebSockets. You set up a WebSocket server in Go, handled WebSocket connections, and broadcasted messages to all connected clients. On the client-side, you used Vue.js to establish a WebSocket connection, send messages, and display received messages in real-time.

Feel free to explore and enhance the app further by adding features such as user authentication, message history, or additional collaboration capabilities.

Have fun building real-time applications with Go and WebSockets!