After building the core CRUD backend in Phase 1, Phase 2 of the Freight Tracker project adds real-time updates using WebSockets. This allows clients to receive live status changes for shipments without needing to refresh the page.
Its continuation of building a full-stack logistics tracking app, this post adds real-time updates using Spring Boot, STOMP, and WebSockets.
๐ Why Real-Time with WebSockets?
In logistics applications, users (like clients, warehouse staff, or support teams) often need instant updates on shipment status. Polling the backend repeatedly isnโt efficient โ WebSockets provide a two-way communication channel where the server pushes updates to connected clients instantly.
Spring Boot supports this via:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
โ๏ธ Setting up WebSockets in Spring Boot
WebSocketConfig.java
We created a new class called WebSocketConfig.java
to enable STOMP messaging and define endpoints:
@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");
registry.setApplicationDestinationPrefixes("/app");
}
}
Two Sides of WebSocket Communication
When using STOMP over WebSockets in Spring, there are two types of message flow:
Client โ Server (incoming messages)
Server โ Client (broadcast messages)
We configure these in two parts:
registerStompEndpoints(): Where clients initially connect (like /ws)
configureMessageBroker(): How messages are routed internally (topics, queues, etc.)
๐ Keywords Explanation:
-
@Configuration
: Marks the class as a Spring config class which meansย it processes the class during startup -
@EnableWebSocketMessageBroker
: Turns on WebSocket messaging with STOMP protocol and enables the broker system (/topic or /queue). It allows you to also map /app/... for client โ server messages -
.addEndpoint("/ws")
: Clients connect to this endpoint -
withSockJS()
: Adds fallback support if browser doesn't support native WebSockets -
enableSimpleBroker("/topic")
: Clients subscribe to these topics to receive updates -
setApplicationDestinationPrefixes("/app")
: Prefix for client-to-server messages
๐ข Broadcasting Status Updates
To send messages to subscribed clients, we created a broadcaster class:
ShipmentStatusBroadcaster.java
@Component
public class ShipmentStatusBroadcaster {
@Autowired
private SimpMessagingTemplate messagingTemplate;
public void broadcastUpdate(ShipmentUpdateMessage message) {
messagingTemplate.convertAndSend("/topic/shipments", message);
}
}
SimpMessagingTemplate
is used to send messages to WebSocket subscribers (like RestTemplate
is used for HTTP).
Why Do We Need SimpMessagingTemplate?
The service has business events (e.g., shipment status updated) and we want to push those events to clients manually โ not in response to a client message, but whenever the backend decides (like after saving to DB).
SimpMessagingTemplate is a helper that talks directly to the broker we created.
SimpMessagingTemplate.convertAndSend() = Pushes events into the broker
๐ง What is @Component
?
This marks the class as a Spring-managed bean. It gets picked up automatically during component scanning and injected wherever needed (like in the ShipmentController).
๐ฆ Updating the Controller
We modified the ShipmentController
to send broadcast messages on every shipment creation or update:
@PostMapping
public Shipment createShipment(@RequestBody Shipment shipment) {
shipment.setLastUpdatedTime(LocalDateTime.now());
Shipment saved = shipmentRepository.save(shipment);
broadcaster.broadcastUpdate(
ShipmentUpdateMessage.builder()
.shipmentId(saved.getId())
.trackingNumber(saved.getTrackingNumber())
.status(saved.getStatus())
.lastUpdatedTime(saved.getLastUpdatedTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
.build()
);
return saved;
}
We do the same in the PUT /api/shipments/{id}
method.
Frontend (subscribe) ----connect---> /ws endpoint
|
| Subscribes to /topic/shipments
v
Spring Broker (enabled by enableSimpleBroker)
|
| convertAndSend("/topic/shipments", dto)
v
Frontend receives update in real-time
๐จ DTO for Real-Time Updates
ShipmentUpdateMessage.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ShipmentUpdateMessage {
private Long shipmentId;
private String trackingNumber;
private ShipmentStatus status;
private String lastUpdatedTime;
}
We keep this DTO separate from the main Shipment model to ensure:
- Lightweight messages
- No overexposing of sensitive data
- Easy control over WebSocket response structure
๐ Frontend Client Example
Hereโs how a basic HTML frontend listens for shipment updates:
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>
<script>
const socket = new SockJS("http://localhost:8080/ws");
const stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log("Connected: " + frame);
stompClient.subscribe("/topic/shipments", function (message) {
const body = JSON.parse(message.body);
console.log(`Shipment #${body.trackingNumber} is now ${body.status} at ${body.lastUpdatedTime}`);
});
});
</script>
Every time a shipment is created or updated, this client will receive a message in real time.
๐ง What I Learned in Phase 2
- How STOMP protocol simplifies WebSocket communication
- How to use
@EnableWebSocketMessageBroker
to configure endpoints - How
SimpMessagingTemplate
is used to push messages - When and why to use DTOs for WebSockets
- How real-time updates improve client experience in logistics
๐๏ธ Coming Next
- React-based frontend dashboard
- Service Layer refactor and error handling
- Deployment to cloud with Docker and AWS
Thanks for reading โ you can find the code here: https://github.com/jayanti-neu/freight-tracker ๐
Top comments (0)