Docs

Reactive endpoints

Reactive endpoints allow you to stream data to the client without the traditional HTTP request-response pattern.
Important
Preview feature
You need to enable the hillaPush feature flag to use reactive endpoints.

Although traditional server calls work fine in most cases, sometimes you need different tools for your job. Reactor is one of these. It can help you in streaming data to clients and fits well into a non-blocking application. Whenever the simple request-response pattern doesn’t suit your needs, you might want to 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 pick up on Reactor, they have a curated learn page.

Although getting comfortable with creation of streaming, non-blocking applications requires some effort, Hilla allows you to take advantage of Reactor in minutes. You can read the excellent blog article on reactive endpoints to get the basics.

Example

Before looking at the API details, you can get to know the basics of reactive endpoints by writing a basic application that can stream the current time of the server to the client. The purpose 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 as usual:

npx @hilla/cli init --push flux-clock

Note the presence of the --push flag; this enables support for push / reactive endpoints.

Now, start your new application as usual:

cd flux-clock
.\mvnw

Then, when the application opens in the browser, click on the Dev Tools icon in the bottom-right corner and then on Experimental features. Double-check that Push support in Hilla is enabled, or enable it if necessary.

Create a server-side endpoint

Open your application in your favorite IDE or editor.

First, create a new endpoint and name it ReactiveEndpoint. You can put it inside HelloWorldEndpoint.java or in a package of your choice. This new endpoint contains these two methods:

@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");
    });
}

You can click on the expand icon above 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 cannot be sent to the client in real time. In this use case, it’s okay to lose some messages.

The second method, getClockCancellable(), returns the same Flux, but wrapped in an EndpointSubscription, 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.

Create a view to shows the messages

Now you get to use these methods in our 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 (frontend/routes.ts), by importing it with:

import './views/reactive/reactive-view';

and declaring it with:

  {
    path: 'reactive-clock',
    component: 'reactive-view',
    icon: 'la la-clock',
    title: 'Server Clock',
  },

right after the hello path.

You new view should now be available in the application. When the Toggle server clock button is clicked, the time will appear in the text field and will update each second. You can stop it by clicking again.

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 to automatically close it when the view is detached.