Reactive Endpoints
Although traditional server calls work fine in most cases, sometimes you need different tools. Reactor is one of these, and can help you stream data to clients — and it fits well into a non-blocking application. Whenever the simple request-response pattern doesn’t suit your needs, you might consider Reactor. Multi-user applications, infinite data streaming, and retries are all good examples of what you can do with it.
If you want to know more about Reactor, see their curated learning page.
Although getting comfortable with creating streaming, non-blocking applications requires some effort, Hilla allows you to take advantage of Reactor in minutes. To learn the basics of reactive endpoints, read the Hilla blog article on the subject.
Familiarizing
Before looking at the API details, you might familiarize yourself with the basics of reactive endpoints by writing a simple application that can stream the current time of the server to the client. The task of this example is to send the server time to all subscribed clients, with an interval of one second between each message.
Start by creating a Hilla application like so:
npx @hilla/cli init --lit flux-clock
Server-Side Endpoints
Next you’ll need to create a server-side endpoint. To do this, open your application in your favorite IDE or editor.
Create a new endpoint in a package of your choice and name it ReactiveEndpoint
. Add these two methods to it:
@AnonymousAllowed
public Flux<@Nonnull String> getClock() {
return Flux.interval(Duration.ofSeconds(1))
.onBackpressureDrop()
.map(_interval -> new Date().toString());
}
@AnonymousAllowed
public EndpointSubscription<@Nonnull String> getClockCancellable() {
return EndpointSubscription.of(getClock(), () -> {
LOGGER.info("Subscription has been cancelled");
});
}
Note
|
You can click on the expand code icon at the top-right of the code snippet to get the whole file.
|
The first method, getClock()
, creates a Flux that streams the server time each second. It uses onBackpressureDrop() to avoid saturation in case some messages can’t be sent to the client in real time. In this use case, it’s acceptable to lose some messages.
The second method, getClockCancellable()
, returns the same Flux
, but wrapped in an EndpointSubscription
. This is a Hilla-specific Flux
wrapper which allows you to execute something on the server side if the subscription is cancelled by the client.
In this example, a log message is produced.
Showing Messages
Next, you’ll need to create a view to show the messages. You’ll use these methods in your client views.
Create the frontend/views/reactive/reactive-view.ts
file to add a new view. This looks like the default "Hello world" example, customized with the code that follows. Again, you can expand it if you prefer to get the whole file. You might have to fix some imports.
render() {
return html`
<vaadin-text-field label="Server time" .value=${this.serverTime}></vaadin-text-field>
<vaadin-button @click=${this.toggleServerClock}>Toggle server clock</vaadin-button>
`;
}
toggleServerClock() {
if (this.subscription) {
this.subscription.cancel();
this.subscription = undefined;
} else {
(this.subscription = ReactiveEndpoint.getClockCancellable()).onNext((time) => {
this.serverTime = time;
});
}
}
Then, add the new view to the router by importing it in frontend/routes.ts
like so:
import './views/reactive/reactive-view';
Declare it as follows:
{
path: 'reactive-clock',
component: 'reactive-view',
icon: 'la la-clock',
title: 'Server Clock',
},
Place this view configuration code right after the hello
path.
API Overview
The generated TypeScript endpoint methods return a Subscription<T>
object, which offers these methods:
cancel()
-
Cancel the subscription.
onNext()
-
Get the next message in the Flux.
onError()
-
Get errors that can happen in the Flux.
onComplete()
-
Be informed when the Flux has no more messages to send.
context()
-
Bind to the context, which allows the application automatically to close it when the view is detached.