How I Built a Reactive Fullstack Monolith with Spring Boot 3.x /4.x and PulsePoint (Without Node.js or React)
Introduction
As Java developers, whenever we need to build a modern, interactive web application, we usually face a painful dilemma:
- The SPA route: Spin up a separate React/Vue/Next.js frontend with npm, Vite, and duplicate DTO types across TypeScript and Java.
- The traditional MPA route: Use Thymeleaf or JSP, sacrificing single-page reactivity for full-page reloads.
Recently, I evaluated PulsePoint v2—a lightweight reactive runtime designed to bridge this exact gap. The goal was to prove whether a Java developer can build a single-page reactive application where Java remains the only backend and build toolchain.
Here is how I built it, the architecture behind it, and what I learned along the way.
The Architecture
Instead of maintaining two separate servers (e.g. Node on port 3000 and Spring Boot on port 8080), the entire application runs as a single monolithic JAR:
Browser (PulsePoint v2 runtime)
│
├─► RPC (POST + X-PP-RPC: true, X-PP-Function, X-CSRF-Token)
├─► SSE Streaming (POST + Accept: text/event-stream)
└─► WebSocket (ws://host/__pulsepoint/ws?name=tasks)
▼
Spring Boot Monolith
│
├─► PulsePointCsrfFilter (Cookie / Header bridge)
├─► Spring Security (Session auth + CSRF validation)
├─► PulsePointRpcFilter (Dispatches RPC & streams SSE chunks)
│ ├─► TaskService (Business Layer & JPA) ──► PostgreSQL
│ └─► TaskBroadcaster (Real-time WebSocket events)
└─► Thymeleaf (Initial HTML Host)
How It Works
1. Zero Build Step Frontend
You download a single static file (pp-reactive-v2.min.js) into src/main/resources/static/js/. Inside your Thymeleaf HTML template, you simply declare:
<script type="module">
import { ComponentInit as PP } from "/js/pp-reactive-v2.min.js";
PP.bootstrap();
</script>
No npm install, no package.json, no bundler configuration.
2. Reactive UI with React Hook Semantics
Components are defined directly in HTML templates with clean, intuitive state hooks:
<template pp-component="task_manager">
<div>
<h3>Tasks ({tasks.length})</h3>
<ul>
<template pp-for="task in tasks">
<li key="{task.id}">{task.title}</li>
</template>
</ul>
<script>
const [tasks, setTasks] = pp.state([]);
pp.effect(() => {
pp.rpc("listTasks").then(setTasks);
}, []);
</script>
</div>
</template>
3. The Java RPC Bridge
PulsePoint issues HTTP POST requests with custom headers (X-PP-RPC: true, X-PP-Function: <functionName>). In Spring Boot, a reusable PulsePointRpcFilter intercepts these requests, looks up the registered Java service method, validates Jakarta Bean annotations (@NotBlank, @Size), and returns clean JSON responses.
Key Challenges & How I Solved Them
CSRF Token Bridge:
Spring Security standardizes onXSRF-TOKENcookies andX-XSRF-TOKENheaders. PulsePoint looks forpp_csrfand sendsX-CSRF-Token.
Solution: Configured Spring Security'sCookieCsrfTokenRepositorywithsetCookieName("pp_csrf")andsetHeaderName("X-CSRF-Token")to make them interoperate seamlessly.Non-JSON Error Responses:
Standard Spring Boot error pages emit HTML. If a 401 or 403 occurs, the PulsePoint client parser expects JSON and throws aSyntaxError.
Solution: Configured JSON-aware Spring Security entry points specifically for requests bearingX-PP-RPC: true.Event Target Recycling:
In modern browsers, accessingevent.currentTarget.reset()afterawait pp.rpc(...)can fail because native event targets are cleared asynchronously.
Solution: Always cacheconst form = event.currentTarget;before anyawaitstatement.
Verdict: Is It Worth It?
- Lightweight & Fast: First-paint is instant because HTML is rendered on the server.
- Single Source of Truth: Validation rules and permissions live in Java DTOs and Spring Security.
-
Great for React Devs & Java Devs: React developers already know
pp.stateandpp.effect, while Java devs never have to leave Maven or their IDE.
If you are building dashboards, internal admin tools, or SaaS portals and want single-page reactivity without the overhead of a JavaScript build toolchain, Spring Boot + PulsePoint is a game changer.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.