This blog will walk you through the steps to create a simple real-time chat application built with Go and PubNub. The application supports multiple clients, allows you to exchange messages instantly and shows when users go online or offline.
Resources
Running the sample application
I created a simple sample available on GitHub, go ahead and clone that application:
git clone https://github.com/PubNubDevelopers/pubnub-go-chat.git
cd pubnub-go-chat
The next step is to obtain PubNub Keys, these keys allow your application to connect to PubNub’s infrastructure and allow the application to send real-time messages as well as check who is online or offline. You can sign up to PubNub for free and get your first set of demo keys without entering a credit card.
Sign up for a free PubNub account at https://admin.pubnub.com
Create a new app in the PubNub Admin Portal
Enable Presence Events on the keyset
Copy the Publish and Subscribe keys from the app's keyset
Next, open the application in your favourite editor and make some changes:
Edit the constants in main.go to define your PubNub keys. You don't need to change the CHAT_CHANNEL value.
const (
PUBLISH_KEY = "Change me: your-publish-key-here"
SUBSCRIBE_KEY = "Change me: your-subscribe-key-here"
CHAT_CHANNEL = "chat-room"
)
Then, install dependencies:
go mod tidy
To test the chat functionality between two clients, open two terminal windows:
Terminal 1:
go run main.go
Terminal 2:
go run main.go
Enter different usernames in each terminal and start chatting!
If you run into any issues, check out the troubleshooting steps in the project readme.
Example Session
🚀 PubNub Go Chat Application
=============================
Enter your username: Alice
Welcome Alice! You're now entering the chat room.
Type 'quit' to exit the chat.
=============================
✅ Connected to PubNub! You can start chatting now.
🟢 Bob joined the chat
You: Hello everyone!
💬 [14:32:15] Bob: Hi Alice! How are you?
You: I'm doing great! How about you?
💬 [14:32:30] Bob: Fantastic! This chat app works perfectly.
You: quit
👋 Goodbye!
Understanding the Sample Application
PubNub Features Used
The sample application uses the following PubNub features:
Publish/Subscribe: Core messaging functionality
Presence: Tracks when users join/leave the chat
Real-time Events: Handles incoming messages and status updates through PubNub channels
Code Walkthrough
All of the code is contained within a single file, main.go
Initialize PubNub, this connects your application to the PubNub network.
// Create PubNub object
config := pubnub.NewConfigWithUserId(pubnub.UserId(username))
config.SubscribeKey = SUBSCRIBE_KEY
config.PublishKey = PUBLISH_KEY
pn := pubnub.NewPubNub(config)
// Create listener for incoming messages
listener := pubnub.NewListener()
This application handles 3 different types of events from PubNub
Firstly, status events are captured to track the application lifecycle
case status := <-listener.Status:
switch status.Category {
case pubnub.PNConnectedCategory:
fmt.Println("✅ Connected to PubNub! You can start chatting now.")
doneConnect <- true
case pubnub.PNDisconnectedCategory:
fmt.Println("❌ Disconnected from PubNub")
case pubnub.PNReconnectedCategory:
fmt.Println("🔄 Reconnected to PubNub")
}
Chat messages are received using a message listener as follows. Note that the message also specifies the username of who sent it as well as the timestamp of when it was sent:
case message := <-listener.Message:
// Handle incoming chat messages
if msg, ok := message.Message.(map[string]interface{}); ok {
msgUsername := ""
msgText := ""
msgTime := ""
if u, exists := msg["username"]; exists {
if uStr, ok := u.(string); ok {
msgUsername = uStr
}
}
if m, exists := msg["message"]; exists {
if mStr, ok := m.(string); ok {
msgText = mStr
}
}
if t, exists := msg["timestamp"]; exists {
if tStr, ok := t.(string); ok {
msgTime = tStr
}
}
// Don't display our own messages
if msgUsername != username {
fmt.Printf("\n💬 [%s] %s: %s\n", msgTime, msgUsername, msgText)
fmt.Print("You: ")
}
}
The application also supports presence, meaning you will be notified when the different chat participants go online or offline. This is tracked using a presence event on the channel as follows:
case presence := <-listener.Presence:
// Handle presence events (users joining/leaving)
if presence.Event == "join" && presence.UUID != username {
fmt.Printf("\n🟢 %s joined the chat\n", presence.UUID)
fmt.Print("You: ")
} else if presence.Event == "leave" && presence.UUID != username {
fmt.Printf("\n🔴 %s left the chat\n", presence.UUID)
fmt.Print("You: ")
}
Finally, register the listener, subscribe to the channel, and wait for incoming connections:
// Add listener to PubNub
pn.AddListener(listener)
// Subscribe to the chat channel with presence
pn.Subscribe().
Channels([]string{CHAT_CHANNEL}).
WithPresence(true).
Execute()
// Wait for connection
<-doneConnect
Messages are sent in the main chat loop, which will read input from the command line and publish it over PubNub as follows:
for {
fmt.Print("You: ")
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
if input == "quit" || input == "exit" {
fmt.Println("👋 Goodbye!")
// Will trigger a presence leave event
pn.UnsubscribeAll()
break
}
if input != "" {
// Create chat message
chatMsg := ChatMessage{
Username: username,
Message: input,
Timestamp: time.Now(),
}
// Publish message to channel
_, status, err := pn.Publish().
Channel(CHAT_CHANNEL).
Message(map[string]interface{}{
"username": chatMsg.Username,
"message": chatMsg.Message,
"timestamp": chatMsg.Timestamp.Format("15:04:05"),
}).
Execute()
if err != nil {
log.Printf("Failed to publish message: %v", err)
} else if status.StatusCode != 200 {
log.Printf("Publish failed with status: %d", status.StatusCode)
}
}
}
Next steps
If you have any issues, or any other questions, please contact our devrel team at devrel@pubnub.com.