java

Build Scalable Real-Time Apps with Spring Boot WebSocket and STOMP

Learn how to build scalable real-time apps with Spring Boot WebSocket and STOMP, from live updates to secure messaging and testing.

Build Scalable Real-Time Apps with Spring Boot WebSocket and STOMP

Lately, I’ve been thinking a lot about the magic behind live features. You know, when you’re editing a document with a colleague and you see their cursor move in real time, or when a dashboard updates without you hitting refresh. That instant connection feels like the future, but for a long time, building it felt like a complex puzzle. It’s why I want to talk about WebSockets, specifically using Spring Boot and STOMP. This isn’t about a simple chat app. It’s about building the kind of robust, scalable real-time backbone that modern applications need. Let’s get into it.

When I first tried to add live updates to an application, I hit walls. HTTP requests were clunky. Polling the server every few seconds was a waste. I needed a true two-way street where the server could talk to the client just as easily as the client could talk to the server. That’s where WebSocket comes in. It provides a persistent connection. But raw WebSocket is low-level. You have to manage everything yourself. This is where STOMP changes the game. Think of STOMP as giving you a common language, a protocol, for structuring messages over that WebSocket pipe.

Why does this matter? Because without a structure, you end up inventing your own. You’ll spend time designing how to say “this is a chat message” versus “this is a cursor position.” STOMP handles that. It introduces ideas like destinations and subscriptions. Your client can subscribe to /topic/document-updates and your server can send messages there. It’s simple and powerful.

Let’s set up a basic configuration in Spring Boot. First, you need the right dependencies in your pom.xml. You’ll want spring-boot-starter-websocket and, for JSON handling, the Jackson libraries.

Here is how you enable WebSocket support with STOMP:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").setAllowedOriginPatterns("*").withSockJS();
    }

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

This code does a few things. It sets up an endpoint at /ws for clients to connect to. The withSockJS() part is a safety net. It allows browsers or networks that block WebSocket to fall back to other techniques. Then, it configures a message broker. Messages sent to destinations starting with /topic or /queue will be handled by the simple in-memory broker. Messages sent to destinations starting with /app will be routed to methods in our controllers.

Have you ever wondered how a message from one user gets to everyone else in a room?

With this setup, it becomes straightforward. Here’s a simple controller method that receives a message and sends it out to all subscribers.

@Controller
public class CollaborationController {

    @MessageMapping("/document.edit")
    @SendTo("/topic/document.updates")
    public DocumentUpdate handleEdit(DocumentUpdate update) {
        // Add any server-side logic here (e.g., validation, persistence)
        update.setTimestamp(Instant.now());
        return update;
    }
}

A client would send a STOMP message to the destination /app/document.edit. The @MessageMapping annotation catches it. The method processes it and, thanks to @SendTo, the returned object is automatically broadcast to all clients subscribed to /topic/document.updates. It’s a clean separation of concerns.

But what about private messages? What if you need to send a notification to just one user, not a whole topic?

Spring has a great feature for this called user destinations. Remember the setUserDestinationPrefix("/user") we could add to the broker config? It enables a special pattern. On the server side, you can send a message to a destination like /user/queue/notifications. Spring will automatically route that to a unique queue for the specific user session. The client subscribes to /user/queue/notifications, and they only get their messages. It’s perfect for alerts or private chat.

Now, here’s a question that kept me up at night: what happens when your single application instance can’t handle the load?

The simple in-memory broker we used won’t work across multiple servers. A user connected to Server A won’t get messages sent from Server B. For a scalable system, you need an external broker. This is where something like Redis comes in. Redis can act as a central message hub for all your application instances.

Changing our configuration to use Redis is a significant step forward. We replace the simple broker with a broker relay.

@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
    registry.enableStompBrokerRelay("/topic", "/queue")
            .setRelayHost("localhost")
            .setRelayPort(61613)
            .setClientLogin("guest")
            .setClientPasscode("guest");

    registry.setApplicationDestinationPrefixes("/app");
}

This configuration tells Spring to connect to a STOMP broker (like RabbitMQ or a Redis server with a STOMP plugin) running on localhost:61613. All topic and queue messages are passed to this external broker. Every instance of your app connects to the same broker. When one server sends a message to /topic/updates, the broker delivers it to all subscribed clients, regardless of which server they are connected to. This is the key to horizontal scaling.

Security is non-negotiable. You can’t just let anyone connect to your WebSocket endpoint. Spring Security integrates with WebSocket. You can secure your endpoints just like you secure HTTP requests. You can also inject the authenticated user’s information into your controller methods.

@MessageMapping("/secured.action")
public void handleSecured(@Payload Command command, Principal principal) {
    String username = principal.getName();
    // Process command knowing who sent it
}

This Principal object comes from the WebSocket handshake, which is an HTTP request. You can authenticate it using standard Spring Security filters.

Building this is one thing. Testing it is another. How do you test that your messages are routed correctly?

Spring provides excellent testing support. You can write an integration test that uses a StompSession to simulate a client. You can connect, subscribe to a destination, send a message, and verify the response.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class WebSocketTest {

    @Test
    public void testDocumentUpdateBroadcast() throws Exception {
        WebSocketStompClient stompClient = new WebSocketStompClient(new SockJsWebSocketClient(new WebSocketClient()));

        StompSession session = stompClient.connectAsync(getWsPath(), new StompSessionHandlerAdapter() {}).get(5, TimeUnit.SECONDS);

        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(1);
        session.subscribe("/topic/document.updates", new StompFrameHandler() {
            @Override public Type getPayloadType(StompHeaders headers) { return String.class; }
            @Override public void handleFrame(StompHeaders headers, Object payload) { blockingQueue.add((String) payload); }
        });

        session.send("/app/document.edit", "Test Payload");

        String response = blockingQueue.poll(5, TimeUnit.SECONDS);
        assertThat(response).contains("Test Payload");
    }
}

This test ensures your entire messaging flow works, from the client send, through the controller, to the broker, and back to the subscriber. It gives you huge confidence.

I find that the real value comes when you stop thinking about “messages” and start thinking about “events.” A cursor move is an event. A character insertion is an event. Your application becomes a stream of these events, shared in real time. This pattern is the foundation for collaboration tools, live sports updates, trading platforms, and interactive games.

The journey from a static page to a live, collaborative experience is exciting. It changes how users interact with your application. They feel connected. The technology, while sophisticated, is accessible thanks to frameworks like Spring Boot. It handles much of the complexity, letting you focus on your application’s unique features.

I hope this look at building real-time systems has been helpful. It’s a powerful tool to have in your development toolkit. Have you built something with real-time features before? What was the biggest challenge you faced? Share your thoughts in the comments below. If you found this guide useful, please like and share it with other developers who might be working on the next generation of interactive applications. Let’s keep the conversation going.


As a best-selling author, I invite you to explore my books on Amazon. Don’t forget to follow me on Medium and show your support. Thank you! Your support means the world!


101 Books

101 Books is an AI-driven publishing company co-founded by author Aarav Joshi. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as $4—making quality knowledge accessible to everyone.

Check out our book Golang Clean Code available on Amazon.

Stay tuned for updates and exciting news. When shopping for books, search for Aarav Joshi to find more of our titles. Use the provided link to enjoy special discounts!


📘 Checkout my latest ebook for free on my channel!
Be sure to like, share, comment, and subscribe to the channel!


Our Creations

Be sure to check out our creations:

Investor Central | Investor Central Spanish | Investor Central German | Smart Living | Epochs & Echoes | Puzzling Mysteries | Hindutva | Elite Dev | JS Schools


We are on Medium

Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Science & Epochs Medium | Modern Hindutva

Keywords: Spring Boot WebSocket, STOMP, real-time applications, scalable messaging, WebSocket testing



Similar Posts
Blog Image
Building Event-Driven Microservices with Spring Cloud Stream: Complete Apache Kafka Integration Guide

Learn to build event-driven microservices with Spring Cloud Stream, Apache Kafka & Schema Registry. Complete guide with code examples, error handling & deployment tips.

Blog Image
Master Event-Driven Microservices: Apache Kafka, Spring Cloud Stream, and Distributed Tracing Guide

Learn to build scalable event-driven microservices using Apache Kafka, Spring Cloud Stream, and distributed tracing. Master schema evolution, error handling, and monitoring patterns for production systems.

Blog Image
HikariCP Performance Optimization: Advanced Spring Boot Connection Pooling Strategies and Monitoring

Master HikariCP connection pooling with Spring Boot. Learn advanced configuration, sizing strategies, monitoring, and performance optimization techniques for high-traffic applications.

Blog Image
Apache Kafka Spring Cloud Stream Integration Guide: Build Scalable Event-Driven Microservices Architecture

Learn how to integrate Apache Kafka with Spring Cloud Stream for scalable event-driven microservices. Build resilient, high-throughput messaging systems effortlessly.

Blog Image
Mastering Java Memory: How to Diagnose and Fix Performance Issues with JFR

Learn how to use Java Flight Recorder and Mission Control to uncover memory leaks, GC pressure, and optimize JVM performance.

Blog Image
Build Event-Driven Microservices: Apache Kafka and Spring Cloud Stream Integration Guide for Enterprise Applications

Learn how to integrate Apache Kafka with Spring Cloud Stream for scalable event-driven microservices. Build resilient, real-time systems easily.