Chat

Build a Chat app in Go using PubNub

0 MIN READ • Darryn Campbell on Jun 12, 2025

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.

Screenshot

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.

  1. Sign up for a free PubNub account at https://admin.pubnub.com

  2. Create a new app in the PubNub Admin Portal

  3. Enable Presence Events on the keyset

  4. 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.