Configuring WebSocket in Spring-Boot and React

Lejdi Prifti
4 min readOct 13, 2023

In this article, I am going to describe step-by-step how you can configure WebSocket in Spring Boot and use it with React.

Dependencies

We only need two dependencies since I’m trying to minimize the number of dependencies required to a minimum.

 <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

Configuration

Next, we will go and create the WebSocketConfig configuration class. It implements the WebSocketMessageBrokerConfigurer and is annotated with Configuration and EnableWebSocketMessageBroker .

Configurationindicates that the class is a Spring configuration class, and it’s used to define Spring beans and configurations for the application.

@EnableWebSocketMessageBrokerenables WebSocket-based messaging within the application and configures it as a message broker.

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/")
.setAllowedOrigins("http://localhost:3000")
.withSockJS();
}
}

Now, let’s look at the methods within the WebSocketConfig class:

Configure the message broker

  • configureMessageBroker(MessageBrokerRegistry config): This method is used to configure the message broker for the WebSocket.

config.enableSimpleBroker("/topic") enables a simple message broker that allows clients to subscribe to and receive messages from the specified destination prefix, which is /topic. Messages sent by the server to topics starting with /topic will be broadcasted to connected clients.

config.setApplicationDestinationPrefixes("/app") sets the application destination prefix. Clients can send messages to destinations that start with this prefix. For example, a client can send a message to /app/destination.

Register the Stomp endpoint

  • registerStompEndpoints(StompEndpointRegistry registry) registers a Stomp (Simple Text Oriented Messaging Protocol) endpoint that clients can connect to.

registry.addEndpoint("/")registers the root endpoint (i.e., "/") as the WebSocket endpoint. Clients can connect to this endpoint to establish WebSocket connections and we will see how in the React app.

withSockJS()enables SockJS support, which is a JavaScript library that provides a WebSocket-like interface for browsers that don't support WebSocket directly. This allows broader client compatibility.

Controller

Let’s create a simple controller.

@Slf4j
@Controller
public class WebSocketController {

private final Map<String, List<UserText>> chats = new HashMap<>();

@MessageMapping("/chat/{chatId}")
@SendTo("/topic/chat/{chatId}")
public List<UserText> sendMessageWithWebsocket(@DestinationVariable String chatId,
@Payload Message<UserText> message) {
log.info("new message arrived in chat with id {}", chatId);
List<UserText> messages = this.chats.getOrDefault(chatId, new ArrayList<UserText>());
messages.add(message.getPayload());
chats.put(chatId, messages);
return messages;
}
}

Here, we store the messages in an array called messages and then store that array in a map with the chat ID as its identifier. Using the annotation @DestinationVariable, we can obtain the chat ID. Every user will subscribe to a different conversation and only view the messages in that chat.

Start your application and run the following command in terminal to check if you can connect successfully to the WebSocket endpoint.

wscat -c ws://localhost:8080/websocket

Build simple React app

To connect to React, we will need the following dependency:

npm install --save @stomp/stompjs

Let’s edit the App.tsx to suit our needs.

import { Client } from '@stomp/stompjs';
import { useEffect, useState } from 'react';
import './App.css';
import logo from './logo.svg';

function App() {
let client: Client | null = null;

const [chatId, setChatId] = useState("")

useEffect(() => {
// Create a WebSocket connection

client = new Client();

// Configure the WebSocket endpoint URL
const websocketUrl = 'ws://localhost:8080/websocket'; // Replace with your WebSocket endpoint URL

// Connect to the WebSocket server
client.configure({
brokerURL: websocketUrl,
debug: function (str) {
console.log(str);
},
onConnect: () => {
// Perform actions after successful connection
const destination = `/topic/chat/${chatId}`; // Specify the destination for the server-side message handler
client && client.subscribe(destination, (message) => {
console.log('Received message:', JSON.parse(message.body));
// Process the received message as needed
});
},
// You can add more event handlers and configuration options as needed
});

// Connect to the WebSocket server
client.activate();


// Clean up the connection on component unmount
return () => {
client && client.deactivate();
};
}, [chatId]);


return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
<input type='text' aria-label='ChatId' onChange={(event) => {
console.log(event)
setChatId(event.target.value)
}}></input>
<button onClick={() => {
const destination = `/app/chat/${chatId}`; // Specify the destination for the server-side message handler
const message = 'Hello, server!'; // Specify the message to send
if (client != null) {
client.publish({
destination,
body: JSON.stringify({
data: message,
userId: 1 // Specify a user ID
}),
});
}
}}>Send</button>
</p>
</header>
</div>
);
}

export default App;

--

--

Lejdi Prifti

Software Developer | ML Enthusiast | AWS Practitioner | Kubernetes Administrator