<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mahendra  S H</title>
    <description>The latest articles on DEV Community by Mahendra  S H (@mahendrash).</description>
    <link>https://dev.to/mahendrash</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1107244%2F8e392971-1fcf-4f13-a868-a58e7e2a0750.png</url>
      <title>DEV Community: Mahendra  S H</title>
      <link>https://dev.to/mahendrash</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mahendrash"/>
    <language>en</language>
    <item>
      <title>How I Built a Reactive Fullstack Monolith with Spring Boot 3.x / 4.x and PulsePoint (Without Node.js or React)</title>
      <dc:creator>Mahendra  S H</dc:creator>
      <pubDate>Fri, 25 Sep 2026 07:04:40 +0000</pubDate>
      <link>https://dev.to/mahendrash/how-i-built-a-reactive-fullstack-monolith-with-spring-boot-3x-4x-and-pulsepoint-without-615</link>
      <guid>https://dev.to/mahendrash/how-i-built-a-reactive-fullstack-monolith-with-spring-boot-3x-4x-and-pulsepoint-without-615</guid>
      <description>&lt;h1&gt;
  
  
  How I Built a Reactive Fullstack Monolith with Spring Boot 3.x /4.x and PulsePoint (Without Node.js or React)
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As Java developers, whenever we need to build a modern, interactive web application, we usually face a painful dilemma:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The SPA route:&lt;/strong&gt; Spin up a separate React/Vue/Next.js frontend with npm, Vite, and duplicate DTO types across TypeScript and Java.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The traditional MPA route:&lt;/strong&gt; Use Thymeleaf or JSP, sacrificing single-page reactivity for full-page reloads.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Recently, I evaluated &lt;strong&gt;PulsePoint v2&lt;/strong&gt;—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 &lt;strong&gt;Java remains the only backend and build toolchain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is how I built it, the architecture behind it, and what I learned along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;Instead of maintaining two separate servers (e.g. Node on port 3000 and Spring Boot on port 8080), the entire application runs as a &lt;strong&gt;single monolithic JAR&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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 &amp;amp; streams SSE chunks)
   │     ├─► TaskService (Business Layer &amp;amp; JPA) ──► PostgreSQL
   │     └─► TaskBroadcaster (Real-time WebSocket events)
   └─► Thymeleaf (Initial HTML Host)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Zero Build Step Frontend
&lt;/h3&gt;

&lt;p&gt;You download a single static file (&lt;code&gt;pp-reactive-v2.min.js&lt;/code&gt;) into &lt;code&gt;src/main/resources/static/js/&lt;/code&gt;. Inside your Thymeleaf HTML template, you simply declare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentInit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;PP&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/js/pp-reactive-v2.min.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;PP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;npm install&lt;/code&gt;, no &lt;code&gt;package.json&lt;/code&gt;, no bundler configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Reactive UI with React Hook Semantics
&lt;/h3&gt;

&lt;p&gt;Components are defined directly in HTML templates with clean, intuitive state hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;pp-component=&lt;/span&gt;&lt;span class="s"&gt;"task_manager"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Tasks ({tasks.length})&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;pp-for=&lt;/span&gt;&lt;span class="s"&gt;"task in tasks"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;key=&lt;/span&gt;&lt;span class="s"&gt;"{task.id}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{task.title}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTasks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
            &lt;span class="nx"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;listTasks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setTasks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The Java RPC Bridge
&lt;/h3&gt;

&lt;p&gt;PulsePoint issues HTTP POST requests with custom headers (&lt;code&gt;X-PP-RPC: true&lt;/code&gt;, &lt;code&gt;X-PP-Function: &amp;lt;functionName&amp;gt;&lt;/code&gt;). In Spring Boot, a reusable &lt;code&gt;PulsePointRpcFilter&lt;/code&gt; intercepts these requests, looks up the registered Java service method, validates Jakarta Bean annotations (&lt;code&gt;@NotBlank&lt;/code&gt;, &lt;code&gt;@Size&lt;/code&gt;), and returns clean JSON responses.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Challenges &amp;amp; How I Solved Them
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CSRF Token Bridge:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Spring Security standardizes on &lt;code&gt;XSRF-TOKEN&lt;/code&gt; cookies and &lt;code&gt;X-XSRF-TOKEN&lt;/code&gt; headers. PulsePoint looks for &lt;code&gt;pp_csrf&lt;/code&gt; and sends &lt;code&gt;X-CSRF-Token&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
&lt;em&gt;Solution:&lt;/em&gt; Configured Spring Security's &lt;code&gt;CookieCsrfTokenRepository&lt;/code&gt; with &lt;code&gt;setCookieName("pp_csrf")&lt;/code&gt; and &lt;code&gt;setHeaderName("X-CSRF-Token")&lt;/code&gt; to make them interoperate seamlessly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Non-JSON Error Responses:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Standard Spring Boot error pages emit HTML. If a 401 or 403 occurs, the PulsePoint client parser expects JSON and throws a &lt;code&gt;SyntaxError&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
&lt;em&gt;Solution:&lt;/em&gt; Configured JSON-aware Spring Security entry points specifically for requests bearing &lt;code&gt;X-PP-RPC: true&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Event Target Recycling:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In modern browsers, accessing &lt;code&gt;event.currentTarget.reset()&lt;/code&gt; after &lt;code&gt;await pp.rpc(...)&lt;/code&gt; can fail because native event targets are cleared asynchronously.&lt;br&gt;&lt;br&gt;
&lt;em&gt;Solution:&lt;/em&gt; Always cache &lt;code&gt;const form = event.currentTarget;&lt;/code&gt; before any &lt;code&gt;await&lt;/code&gt; statement.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Verdict: Is It Worth It?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight &amp;amp; Fast:&lt;/strong&gt; First-paint is instant because HTML is rendered on the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single Source of Truth:&lt;/strong&gt; Validation rules and permissions live in Java DTOs and Spring Security.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Great for React Devs &amp;amp; Java Devs:&lt;/strong&gt; React developers already know &lt;code&gt;pp.state&lt;/code&gt; and &lt;code&gt;pp.effect&lt;/code&gt;, while Java devs never have to leave Maven or their IDE.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building dashboards, internal admin tools, or SaaS portals and want single-page reactivity without the overhead of a JavaScript build toolchain, &lt;strong&gt;Spring Boot + PulsePoint is a game changer&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>fullstack</category>
      <category>java</category>
      <category>springboot</category>
    </item>
  </channel>
</rss>
