REST and Services
|
This section documents the current Vaadin release line — Vaadin 24 LTS / 25.x, Java 17+, Spring Boot 3 / Jakarta EE 10 — as published at the official Vaadin documentation, which is the reference these pages are written and verified against. No specific patch version is pinned. Flow (server-side Java) is the authoring style used throughout, with Hilla / React shown where it differs; Vaadin 7 and the pre-Flow architecture appear only as migration contrast. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since Vaadin ships major releases roughly twice a year and its ecosystem iterates. This section’s bibliography lists the reference material consulted while preparing these pages. |
A Vaadin UI talks to the server through Vaadin’s own channel, so it needs no REST API for itself. REST still matters at the edges: exposing data to other clients, and consuming third-party APIs. This page follows the REST API guide and Business Logic.
A service layer
Put domain logic in @Service beans, not in views. A view is a thin adapter: gather input, call a service,
render the result. The same service then backs a Flow view, a Hilla endpoint
(Hilla and React Views), a REST controller, and a scheduled job — one implementation,
many entry points.
@Service
public class InvoiceService {
@Transactional
public Invoice issue(IssueInvoiceCommand command) { /* ... */ }
public List<Invoice> outstanding() { /* ... */ }
}
Exposing REST alongside Flow
A Spring @RestController and Flow routes live in the same application under different paths — Flow serves
the UI, the controller serves /api/**:
@RestController
@RequestMapping("/api/invoices")
public class InvoiceController {
private final InvoiceService service;
public InvoiceController(InvoiceService service) { this.service = service; }
@GetMapping
public List<Invoice> outstanding() { return service.outstanding(); }
@PostMapping
public ResponseEntity<Invoice> issue(@RequestBody @Valid IssueInvoiceCommand cmd) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.issue(cmd));
}
}
Secure /api/** in the same SecurityFilterChain as the Vaadin routes — see Security.
If a browser client on another origin will call this API, configure CORS deliberately;
What is CORS? explains the exchange.
Calling an external API from a view
Do the call on a background thread, then push the result with UI.access(…) so the UI thread is never
blocked (Server Push):
private final RestClient rest = RestClient.create("https://api.example.com");
private void loadRates() {
UI ui = UI.getCurrent();
executor.execute(() -> {
Rates rates = rest.get().uri("/rates").retrieve().body(Rates.class);
ui.access(() -> {
table.setItems(rates.entries());
spinner.setVisible(false);
});
});
}
Use WebClient where you want reactive streaming, RestClient (or the older RestTemplate) for a plain
blocking call. Keep the base URL and credentials in configuration, not in the view.
Endpoint or REST?
| Use | When |
|---|---|
Hilla |
The consumer is your own Hilla / React frontend — you get a generated, type-safe client and shared validation with no API contract to hand-maintain. |
REST |
The consumer is a third party, a mobile app, another service, or anything that needs a stable, documented HTTP contract (OpenAPI, versioned paths). |
See also
-
Hilla and React Views — type-safe endpoints for your own frontend.
-
Server Push — pushing an async API result into the UI.
-
Security — one filter chain for the UI and the API.
-
What is CORS? — cross-origin calls from a browser client.
-
REST API — the official guide.