Skip to main content

WebSub Hub Service

A WebSub hub is the intermediary in the WebSub publish-subscribe protocol. It accepts topic registrations from publishers, manages subscriber lifecycles, and distributes content updates to all verified subscribers when new content is published.

note

Creating a WebSub hub service requires Ballerina code. Once the service exists, you can use the visual designer to implement logic for individual event handlers.

Creating a WebSub hub service

A WebSub hub service listens on a websubhub:Listener and implements remote functions for each hub event — topic registration, subscription management, and content distribution.

import ballerina/websubhub;

configurable int port = 9090;

listener websubhub:Listener hubListener = new (port);

service /hub on hubListener {

remote function onRegisterTopic(websubhub:TopicRegistration msg)
returns websubhub:TopicRegistrationSuccess|websubhub:TopicRegistrationError|error {
// Validate and persist topic registration
return websubhub:TOPIC_REGISTRATION_SUCCESS;
}

remote function onDeregisterTopic(websubhub:TopicDeregistration msg)
returns websubhub:TopicDeregistrationSuccess|websubhub:TopicDeregistrationError|error {
// Remove topic registration
return websubhub:TOPIC_DEREGISTRATION_SUCCESS;
}

remote function onUpdateMessage(websubhub:UpdateMessage msg)
returns websubhub:Acknowledgement|websubhub:UpdateMessageError|error {
// Distribute content update to all topic subscribers
return websubhub:ACKNOWLEDGEMENT;
}

remote function onSubscription(websubhub:Subscription msg)
returns websubhub:SubscriptionAccepted|error {
// Accept and store the subscription request
return websubhub:SUBSCRIPTION_ACCEPTED;
}

remote function onSubscriptionIntentVerified(websubhub:VerifiedSubscription msg)
returns error? {
// Persist the verified subscription
}

remote function onUnsubscription(websubhub:Unsubscription msg)
returns websubhub:UnsubscriptionAccepted|websubhub:BadUnsubscriptionError|
websubhub:InternalUnsubscriptionError|error {
return websubhub:UNSUBSCRIPTION_ACCEPTED;
}

remote function onUnsubscriptionIntentVerified(websubhub:VerifiedUnsubscription msg)
returns error? {
// Remove the persisted subscription
}
}

Hub event callbacks

CallbackTriggerTypical use
onRegisterTopicPublisher registers a new topicValidate and store the topic
onDeregisterTopicPublisher removes a topicClean up topic and associated subscribers
onUpdateMessagePublisher sends a content updateDistribute the update to all topic subscribers
onSubscriptionSubscriber sends a subscription requestValidate and accept the subscription
onSubscriptionValidationAdditional validation step (optional)Enforce custom subscription rules
onSubscriptionIntentVerifiedHub verifies subscriber intentPersist the verified subscription
onUnsubscriptionSubscriber sends an unsubscription requestAccept and process the unsubscription
onUnsubscriptionValidationAdditional unsubscription validation (optional)Enforce custom unsubscription rules
onUnsubscriptionIntentVerifiedHub verifies unsubscription intentRemove the persisted subscription

Optional validation callbacks

The onSubscriptionValidation and onUnsubscriptionValidation callbacks are optional. If implemented, the hub invokes them after onSubscription or onUnsubscription (respectively) and before intent verification. Use them to enforce custom business rules — for example, restricting subscriptions to known topics or authorized callers.

Return nil (or omit a return) to accept the request. Return a SubscriptionDeniedError or UnsubscriptionDeniedError to reject it.

remote function onSubscriptionValidation(websubhub:Subscription msg)
returns websubhub:SubscriptionDeniedError|error? {
// Reject subscriptions to unregistered topics
lock {
if !topics.hasKey(msg.hubTopic) {
return error websubhub:SubscriptionDeniedError("topic not registered");
}
}
// Returning nil accepts the subscription
}

remote function onUnsubscriptionValidation(websubhub:Unsubscription msg)
returns websubhub:UnsubscriptionDeniedError|error? {
// Reject if the subscription does not exist
lock {
if !subscriptions.hasKey(msg.hubCallback) {
return error websubhub:UnsubscriptionDeniedError("subscription not found");
}
}
}

For the complete callback lifecycle and additional configuration options, see the Ballerina WebSubHub specification.

Implementing hub logic

Implement each callback as a remote function inside the hub service. The example below shows a hub that stores topics and subscriptions in isolated maps and distributes updates using a websubhub:HubClient.

import ballerina/websubhub;
import ballerina/log;

isolated map<websubhub:TopicRegistration> topics = {};
isolated map<websubhub:VerifiedSubscription> subscriptions = {};

service /hub on hubListener {

remote function onRegisterTopic(websubhub:TopicRegistration msg)
returns websubhub:TopicRegistrationSuccess|error {
lock {
topics[msg.topic] = msg;
}
log:printInfo("Topic registered", topic = msg.topic);
return websubhub:TOPIC_REGISTRATION_SUCCESS;
}

remote function onDeregisterTopic(websubhub:TopicDeregistration msg)
returns websubhub:TopicDeregistrationSuccess|error {
lock {
_ = topics.remove(msg.topic);
}
return websubhub:TOPIC_DEREGISTRATION_SUCCESS;
}

remote function onUpdateMessage(websubhub:UpdateMessage msg)
returns websubhub:Acknowledgement|error {
lock {
foreach websubhub:VerifiedSubscription sub in subscriptions {
if sub.hubTopic == msg.hubTopic {
websubhub:HubClient hubClient = check new (sub);
check hubClient->notifyContentDistribution({
content: msg.content
});
}
}
}
return websubhub:ACKNOWLEDGEMENT;
}

remote function onSubscription(websubhub:Subscription msg)
returns websubhub:SubscriptionAccepted|error {
return websubhub:SUBSCRIPTION_ACCEPTED;
}

remote function onSubscriptionIntentVerified(websubhub:VerifiedSubscription msg)
returns error? {
lock {
subscriptions[msg.hubCallback] = msg;
}
log:printInfo("Subscription verified", callback = msg.hubCallback);
}

remote function onUnsubscription(websubhub:Unsubscription msg)
returns websubhub:UnsubscriptionAccepted|error {
return websubhub:UNSUBSCRIPTION_ACCEPTED;
}

remote function onUnsubscriptionIntentVerified(websubhub:VerifiedUnsubscription msg)
returns error? {
lock {
_ = subscriptions.remove(msg.hubCallback);
}
}
}

Designing logic with the visual designer

Although WebSub hub service creation is not supported in the visual designer, you can use it to implement logic for event handlers defined in code. Once the service exists in the project, it appears in the Entry Points sidebar and on the design canvas.

Flow designer for the GET upgrade resource showing the Return step with new ChatService()

Click the service node (or the service name in the sidebar) to open the WebSub Hub Service designer, which lists the event handlers.

Flow designer for the GET upgrade resource showing the Return step with new ChatService()

Click any handler row (for example, onUpdateMessage) to open its flow designer view, where you can define the integration logic visually.

Flow designer for the GET upgrade resource showing the Return step with new ChatService()

note

Not all WebSub hub service configuration options are available through the visual designer. For full control — including listener configuration and content distribution settings — use Ballerina code directly.

For more details

See the Ballerina WebSubHub specification for the complete language-level reference, including TLS configuration, authentication, and advanced hub configuration.