Building a Chat Server with Go

Are you thinking of building a chat application in Go? You’ve come to the right place! This post will walk you through everything you need to know to make a chat app with the Stream Chat API and will show a working example that ties all the concepts discussed in this tutorial together.

The source code used for this tutorial can be found on GitHub if you'd like to take a peek at the finished product.

What Does a Chat Server Do?

For a chat server to be useful, it must be capable of performing at least the following tasks:

  • Receiving messages from client applications and distributing them to other clients
  • Broadcasting general notifications to all clients, such as those for when a user joins or leaves a channel
  • Raising events so that a client can make the appropriate response to an event
  • Managing the process of moderating users of the application

Stream Chat can do all these (and much more!), but we’ll only consider a subset of the features available in this article.

Signing Up for Stream

Before we continue, make sure to sign up for a free Stream account here and create a new application once you are signed in. You will find your application keys at the bottom of your Stream Dashboard:

Screenshot of App Access Keys section of the Stream Dashboard

Set these keys aside (and keep them safe!); we'll be using them later to authenticate our app.

Setting Up the SDK

Install the official Golang API client for Stream with the following command:

$ go get github.com/GetStream/stream-chat-go/v2

You can import it into your project as follows:

https://gist.github.com/nparsons08/3f53e7a8a254d4abfdb920bb03088f3a

To instantiate the client object, you need to pass in the application key and secret that you retrieved from your dashboard. Ideally, you’d have them stored as environmental variables in a .env file at the root of your project, so that they are not publicly viewable:

https://gist.github.com/ayoisaiah/e41af888e9bb497a3063554dc95555dc

You can use godotenv to load the configuration variables from your .env file, and create a new Stream client with the code below:

https://gist.github.com/ayoisaiah/8b98d306141e5426dd13ca09e5c8bf84

Creating and Updating Users

To create or update users, Stream exposes the client.UpdateUser method. Here’s how you can use it to create a new user in your app:

https://gist.github.com/nparsons08/a3c01be5f43f38c67fdde7f58a30e650

By default, users are assigned the user role, but you can change it to any of the available channel roles including admin, channel_member, moderator, guest, or anonymous. You can also configure other properties such as name, image (avatar), and a map of user-related properties:

https://gist.github.com/BrightnBubbly/e6ff228242cfd47d7287772caffe08b4

If you want to create or update multiple users at once, you can use the client.UpdateUsers method, which expects any number of Users. The updated info will be returned as a map of Users.

https://gist.github.com/nparsons08/5ee98530cfbaf9d9bdf1be10ab0ed16d

Generating Authentication Tokens

Before a user is allowed to interact with your application, you need to generate a token that you’ll send back to the client-side to prove that the user was successfully authenticated.

To create a user token, you need to pass in the ID of the user, and the expiration time of the token:

https://gist.github.com/nparsons08/cdd178f3c9c6d30d0da3e725a4a1b2e2

The token that is returned will be a byte slice, which you can convert to a string using string(token).

Querying Users

Stream allows you to fetch a user, or group of users, based on some query parameters. The example below shows how you can retrieve the details for three users in one API call:

https://gist.github.com/BrightnBubbly/3d665360e56758dc7bdde7e55e5eea39

The QueryUsers method expects a QueryOption argument, in which you can specify filters, which will determine what set of users will be returned. Here’s another example showing how to retrieve banned users:

https://gist.github.com/BrightnBubbly/83ea903727bd43f28857bdb509158307

You can check out this link for other query filters available to you. The QueryOption argument also supports a Limit property, which you can use to limit the number of results, and an Offset property, which you can use for pagination.

Channels

Channels are an essential aspect of Stream’s Chat API. A "channel" is a single place for users to share messages, reactions, and files, and there are five types of channels available, by default, which are customized for different use cases.

The five default types of channels are:

  • Livestream: Chat for services, such as Twitch
  • Messaging: Configured for apps, such as WhatsApp or Facebook Messenger
  • Team: Good defaults for group messaging apps, such as Slack
  • Gaming: Configured for in-game chat
  • Commerce: Good defaults for building a live chat service, such as Intercom

Let’s assume you are building some sort of Slack clone. You might want to create a General channel where new users are added to by default. Here’s how you can do so with Stream:

https://gist.github.com/BrightnBubbly/d0857aa3526d9faae011d63e13136f27

The first argument to CreateChannel is the channel type discussed above, followed by the ID of the channel, and the ID of the user creating this channel. The final argument is the channel data, which can contain anything you want, except for the following reserved fields: name, image, and members. One thing to note here is that the extra data for the channel must not exceed 5KB in size.

Next, let’s look at a few things you can do with a channel once it has been initialized.

Adding Members to a Channel

To add users as members of a channel, use the AddMembers method on the channel, as shown below:

https://gist.github.com/BrightnBubbly/9ffdfb80806f343db32c8e2b2829e550

The Message object allows you to show a system message on the channel to indicate that something has changed. The above code will have the following effect in the application UI if you use Stream’s React components for your frontend.

A status message is displayed in the application UI

Remove Members from a Channel

Removing users from a channel is as easy calling the RemoveMembers method. It works the same way as AddMembers:

https://gist.github.com/BrightnBubbly/5f036dcab2ad06822a8b22d1ee931f46

A status message is displayed in the application UI

Updating a Channel

You can change a channel’s properties by calling the Update method. You'll need to pass a map that contains the new channel information, as well as a message object that indicates what changed or nil if you don’t want to show a message.

Here’s how that works:

https://gist.github.com/BrightnBubbly/be9fae7d33cda8701eeeb6e091b6cd02

You can also change things like the channel image and roles for its members. If you mark a channel as frozen, new messages sent to the channel will be blocked.

Moderating a Channel

You can set users as moderators for a channel. This permits them to ban any user who does not adhere to the set standards for the channel or application. To give moderation capabilities to any user, use the AddModerators method:

https://gist.github.com/nparsons08/453f2d3179e52ffc7f595967c40ba194

If you want to produce a system message after making a user a moderator, use the AddModeratorsWithMessage method:

https://gist.github.com/BrightnBubbly/dc708363a611bb7d4ea8428b6410d85e

A moderator can ban a user using the BanUser method:

https://gist.github.com/BrightnBubbly/e15fdf82be3823af358d648c2d9c4b45

The first argument is the userID to be banned, while the second is the userID of the admin or moderator who is effecting the ban. You can set the reason for the ban, and a duration for how long the ban should last in the final argument to BanUser. By default, a ban is indefinite.

Once a user is banned, they are no longer able to send messages to the channel

To restore channel access to a banned user, use the UnBanUser method:

https://gist.github.com/nparsons08/cd789728cb0a7b2c07117edf0f23044a

If you want to ban or unban a user from the app entirely, and not just a specific channel, use the BanUser and UnBanUser methods on the client instead.

https://gist.github.com/BrightnBubbly/681a7ebcbadf9ac9df331edfbe307859

You can read more about the moderation tools available to you here.

Sending Messages

To send messages to a channel, you can use the SendMessage method. It accepts a Message object and the userID of the sender. Here’s the simplest example of how to send a text message with Stream chat:

https://gist.github.com/nparsons08/047911af0faea5aef551303094e7e40f

And here’s a more complicated example showing how you can attach an image, and mention a user in a message:

https://gist.github.com/BrightnBubbly/4072b40493cb698f4305339252b4e7c9

If you use Stream’s React components for your application UI, the message would be displayed in this manner

Working Example

Here’s a working example that creates a new user, generates an authentication token for the user, and automatically adds them to a default "General" channel:

https://gist.github.com/ayoisaiah/95a8528403e1144e273d5b207511b7a2

To test it out, paste this code into your main.go file, and start the server using go run main.go.

Note: Make sure that your .env file is configured correctly before starting the server.

Send a POST request to the /join endpoint using Postman, as shown below. You will receive a response with the user details, authentication token for the user, and API key if all goes well:

Screenshot of Postman Example

Wrapping Up

Building a chat server is a complex undertaking, but with Stream’s Chat service, it’s much easier to get one up and running quickly. We’ve only scratched the surface of all the features available to you in this post, so be sure to check the chat docs, as well as the Go SDK docs if you want to furnish your server with additional functionality.

Thanks for reading and happy coding!

TutorialsChat