Skip to main content

Simple Chat Demo

In this guide we will explore a simple chat application built using the SignalWire SDK.

A screenshot of the chat application showing messages sent from multiple users, including timestamps.

The chat application that we are going to build.

The Frontend

Using the SignalWire JavaScript SDK you can easily integrate chat features into any web application. It only takes a few minutes to set up a basic example.

We have prepared for you a simple live example which will be the reference implementation for this guide. Please check it out here.

Connection

To build your own chat application, you first need to include the SDK in your HTML.

<!-- Import SignalWire library -->
<script src="https://unpkg.com/@signalwire/js@3"></script>

Then you can interact with the SDK using the global variable SignalWire. We'll mainly be interested in the SignalWire.Chat.Client class for this guide, but if you'd like to explore this API, feel free to browse the SDK documentation.

To get started, we need to instantiate a Client object and then subscribe to the channels that we want to be part of.

const chatClient = new SignalWire.Chat.Client({
token: token
})

try {
await chatClient.subscribe(channels) // channels is an array such as ['office', 'test']
} catch (error) {
console.error('Error', error)
}

The Client constructor takes a token parameter. This is an authentication token that defines (among other things) the channels which the client is allowed to read and write. When you call chatClient.subscribe, you must make sure that the channels you're subscribing to are allowed by the token.

How to obtain a token?

Tokens are provided to the client by your own custom server. Your server determines whether the user is actually authorized to access the chat and, if they are, asks SignalWire to emit a token. We will see later how to write such server. For now, we will get a token from a public demo server.

Our demo server is located at https://2u5lf.sse.codesandbox.io:8080. To obtain the Chat Token from our demo server, we can perform a POST request to the /get_chat_token endpoint as shown below.

const reply = await axios.post("https://2u5lf.sse.codesandbox.io:8080/get_chat_token", {
member_id: memberId,
channels: channels
});

const token = reply.data.token;

Notice how we specify a member_id (which can be any unique string of your choice) and a list of channels that should be allowed in the token. This interface is not specific to the SignalWire SDK: when you will write your own server, you will be free to specify any parameters you need for the /get_chat_token endpoint.

This is all you need to get the chat up and running.

Downloading the existing messages

After you join a channel, you will likely want to download a list of messages that were sent into that channel. SignalWire stores the messages for you so that you can use the getMessages method of the SDK to get the JSON list. Here is how we do it in the live example:

/**
* Download a list of existing messages from the server.
* @param {string} channel
*/
async function downloadExistingMessages(channel) {
const messages = await chatClient.getMessages({
channel: channel
});

if (!messages?.messages) return;

for (const msg of messages.messages.reverse()) {
displayMessage(msg, channel);
}
}

// Download the already existing messages
for (const channel of channels) {
downloadExistingMessages(channel);
}

For each of the channels we're subscribing to, we call chatClient.getMessages. For each message, we then call displayMessage to display it in the UI.

Receiving incoming messages

If you want to receive incoming messages for the channels you've subscribed to, you just have to listen to the message event. For example:

/**
* Subscribe to the "message" event.
* This is triggered each time a new message is sent in one of
* the channels we're subscribed to.
*/
chatClient.on("message", (message) => {
displayMessage(message, message.channel);
});

This will call displayMessage each time a new message is received.

Sending a message

Sending a message is just a method call. This will send your message into the specified channel:

await chatClient.publish({
channel: channel,
content: message
});

Note that your message doesn't necessarily have to be a string! It can be any JSON-serializable object.

Displaying "is typing" indicator

To display a "member is typing" indicator we can exploit the member state management of the SDK. Each member has an associated state, which can be controlled from the SDK. We can use this state to store a boolean which indicates whether a member is currently typing. The state is shared across all members, so they can update their UI to show the indicator.

To set the typing state, you will need to subscribe to the keyup event for the textarea in which users type their messages. Please refer to the live example for details on this. At the end, here is how the state is updated:

chatClient.setMemberState({
channels: channels, // list of channels
state: {
typing: true
}
});

You can specify any JSON-serializable object as your state. The other members can subscribe to the member.updated event to get the state updates. For example:

// Set of ids of the members who are typing
const typingMemberIds = new Set();

chatClient.on("member.updated", (member) => {
if (member.state?.typing) {
typingMemberIds.add(member.id);
} else {
typingMemberIds.delete(member.id);
}
});

Here, we check the value of member.state.typing. If it's true, we add the member to the set of members who are currently typing. Otherwise, we remove it.

Wrap up

We have built a basic chat application. With just a few lines of code, our application can send and receive messages, subscribe to multiple channels at the same time, access the message history, and display typing indicators.

Here are a few resources to learn more about the chat:

Sign Up Here

If you would like to test this example using your private credentials, you can create a SignalWire account and Space here.

Please feel free to reach out to us on our Community Slack or create a Support ticket if you need guidance!