In this post, I’ll explain to you how to send notifications to the web client without refreshing the web browser.
We are going to create a simple real-time web application that demonstrates how to use Kafka as a message broker with Spring Boot as the backend and Angular 9 on the front end.
Setup Kafka instance
Many installation methods are recommended in official documents. Here we are using docker compose to easily setup a kafka instance.
version: '3.7'
services:
zoo1:
image: zookeeper:3.4.9
hostname: zoo1
ports:
- "2181:2181"
environment:
ZOO_MY_ID: 1
ZOO_PORT: 2181
ZOO_SERVERS: server.1=zoo1:2888:3888
kafka1:
image: confluentinc/cp-kafka:5.5.1
hostname: kafka1
ports:
- "9092:9092"
- "29092:29092"
environment:
KAFKA_ADVERTISED_LISTENERS: LISTENER_DOCKER_INTERNAL://127.0.0.1:29092,LISTENER_DOCKER_EXTERNAL://127.0.0.1:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: LISTENER_DOCKER_INTERNAL:PLAINTEXT,LISTENER_DOCKER_EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: LISTENER_DOCKER_INTERNAL
KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181"
KAFKA_CREATE_TOPICS: notification:1:1:compact
KAFKA_BROKER_ID: 1
KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO"
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
depends_on:
- zoo1
Start the zookeeper and Kafka instance with docker-compose up -d
Backend side code
Create 2 Spring Boot projects (producer Kafka and consumer Kafka). You need to import the Kafka dependencies into your pom.xml file.
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>

The next step is to create an endpoint and a class of service to send the messages to the Kafka topic.

Similar to KafkaProducerConfiguration.java we need to have a Consumer Config to enable the consumer to find the broker. For the consumer project, we have two important configuration files (KafkaConsumerConfiguration.java and WebSocketConfig.java).
Step 1: First, we need to add the WebSocket dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Step 2. Then, we can configure Spring to enable WebSocket and STOMP messaging

WebSocketConfig is annotated with @Configuration to indicate that it is a Spring configuration class. It is also annotated with @EnableWebSocketMessageBroker. As its name suggests, @EnableWebSocketMessageBroker enables WebSocket message handling, backed by a message broker.
The configureMessageBroker() method implements the default method in WebSocketMessageBrokerConfigurer to configure the message broker. It starts by calling enableSimpleBroker() to enable a simple memory-based message broker to carry the greeting messages back to the client on destinations prefixed with /topic.
It also designates the /app prefix for messages that are bound for methods annotated with @MessageMapping. This prefix will be used to define all the message mappings.
The registerStompEndpoints() method registers the /ws-notification endpoint, enabling SockJS fallback options so that alternate transports can be used if WebSocket is unavailable. The SockJS client will attempt to connect to /ws-notification and use the best available transport (websocket, xhr-streaming, xhr-polling, and so on).
We have handled the CORS issue with the following code:
...setAllowedOrigins("http://localhost:4200").withSockJS();
In production you can replace http://localhost:4200 with .setAllowedOrigins(*) or your domain name.
Now we need to create the Front-End part of the application to start the socket communication.
Frontend Development side with Angular
ng new web-notification
cd web-notification
Install dependencies
- npm install — save ng-push
- npm install stompjs
- npm install sockjs-client
Let’s create the websocket.service.ts service file

Launch the front-end application and backend services
ng serve
When the application is launched, the browser asks the user to allow confirmation of push notifications.
Final Result
http://localhost:8082/swagger-ui.html


That’s it.
Thank you for taking the time to read this. The source code can be found here