<?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>Forem: RjS</title>
    <description>The latest articles on Forem by RjS (@rjs).</description>
    <link>https://forem.com/rjs</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1793909%2Fb9774226-6a2d-4508-847d-9853be19da30.png</url>
      <title>Forem: RjS</title>
      <link>https://forem.com/rjs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rjs"/>
    <language>en</language>
    <item>
      <title>Designing a Real-Time React Playground with Workers, Queues, and WebSockets</title>
      <dc:creator>RjS</dc:creator>
      <pubDate>Sat, 04 Apr 2026 13:52:22 +0000</pubDate>
      <link>https://forem.com/rjs/designing-a-real-time-react-playground-with-workers-queues-and-websockets-4h8f</link>
      <guid>https://forem.com/rjs/designing-a-real-time-react-playground-with-workers-queues-and-websockets-4h8f</guid>
      <description>&lt;p&gt;I built a full-stack SaaS project, an interactive playground to learn and practice React. Think of it as &lt;strong&gt;LeetCode, but for React&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Of course, the usual full-stack features are there (authentication, etc.), but that’s not the interesting part.&lt;/p&gt;

&lt;p&gt;What &lt;em&gt;is&lt;/em&gt; interesting is the system design and the problems I ran into while building and deploying it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Makes This Project Different
&lt;/h2&gt;

&lt;p&gt;This project involves several non-trivial concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pub/Sub architecture&lt;/li&gt;
&lt;li&gt;Queues and workers&lt;/li&gt;
&lt;li&gt;WebSockets for real-time updates&lt;/li&gt;
&lt;li&gt;Babel (standalone) for transpilation&lt;/li&gt;
&lt;li&gt;Puppeteer for code execution&lt;/li&gt;
&lt;li&gt;Docker for environment consistency&lt;/li&gt;
&lt;li&gt;AWS for deployment&lt;/li&gt;
&lt;li&gt;Isolated sandbox for safe execution&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Core Flow
&lt;/h2&gt;

&lt;p&gt;Here’s how the system works:&lt;/p&gt;

&lt;p&gt;1: User submits code&lt;br&gt;
2: Backend returns a solutionId&lt;br&gt;
3: Frontend registers a WebSocket using the solutionId&lt;br&gt;
4: Worker starts execution&lt;br&gt;
5: Worker finishes execution&lt;br&gt;
6: Redis publishes the result&lt;br&gt;
7: Backend receives the result&lt;br&gt;
8: Backend emits the result via WebSocket&lt;br&gt;
9: Frontend receives the result and updates the UI&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Transpile Twice?
&lt;/h2&gt;

&lt;p&gt;You might wonder:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If we already transpile on the frontend, why do it again on the backend?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After transpiling on the frontend, if we pass that transpiled code to the backend, we lose the ability to properly simulate user interactions on it.&lt;/p&gt;

&lt;p&gt;For validation, we need to simulate events (like clicks). To do that reliably:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We take the original user code&lt;/li&gt;
&lt;li&gt;Transpile it again on the backend&lt;/li&gt;
&lt;li&gt;Execute it inside Puppeteer&lt;/li&gt;
&lt;li&gt;Trigger events programmatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures we can accurately simulate interactions and validate behaviour.&lt;/p&gt;


&lt;h2&gt;
  
  
  Sandbox Execution
&lt;/h2&gt;

&lt;p&gt;Running arbitrary user code is dangerous.&lt;/p&gt;

&lt;p&gt;To handle this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code is executed inside a &lt;strong&gt;controlled Puppeteer environment&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Each execution happens in a &lt;strong&gt;new isolated page&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;This prevents malicious scripts from affecting the system&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Deployment Challenges
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Problem 1: Puppeteer Dependencies
&lt;/h3&gt;

&lt;p&gt;Puppeteer requires Chrome and several system-level dependencies.&lt;/p&gt;

&lt;p&gt;Most backend hosting platforms don’t provide these out of the box.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution: Docker
&lt;/h3&gt;

&lt;p&gt;Docker allowed me to define a consistent environment with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chrome dependencies&lt;/li&gt;
&lt;li&gt;Node environment&lt;/li&gt;
&lt;li&gt;All required libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now the app runs the same everywhere.&lt;/p&gt;


&lt;h3&gt;
  
  
  Problem 2: Render Deployment Failure
&lt;/h3&gt;

&lt;p&gt;Initially, I deployed the backend on &lt;strong&gt;Render&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But I kept getting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package dotenv not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tried:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reinstalling dependencies&lt;/li&gt;
&lt;li&gt;Modifying the Dockerfile&lt;/li&gt;
&lt;li&gt;Adding installation scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing worked.&lt;/p&gt;




&lt;h3&gt;
  
  
  Switching to Railway
&lt;/h3&gt;

&lt;p&gt;I moved to &lt;strong&gt;Railway&lt;/strong&gt;, and guess what? It worked immediately.&lt;/p&gt;

&lt;p&gt;But there was a catch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only &lt;strong&gt;1 month free trial&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this wasn’t a long-term solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Moving to AWS
&lt;/h2&gt;

&lt;p&gt;This is where things got real.&lt;/p&gt;

&lt;p&gt;I had theoretical knowledge of AWS, but this was my first practical experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I did:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Launched an EC2 instance&lt;/li&gt;
&lt;li&gt;Configured a static IP&lt;/li&gt;
&lt;li&gt;Set up security groups&lt;/li&gt;
&lt;li&gt;Learned SSH (the hard way)&lt;/li&gt;
&lt;li&gt;Cloned the backend repo&lt;/li&gt;
&lt;li&gt;Configured environment variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This process involved a few trials and errors, but it worked.&lt;/p&gt;




&lt;h2&gt;
  
  
  Problem 3: No HTTPS
&lt;/h2&gt;

&lt;p&gt;AWS provides a public DNS, but it’s &lt;strong&gt;HTTP&lt;/strong&gt;, not HTTPS. This becomes a problem because if the frontend is served over HTTPS, the browser expects all backend requests to also use HTTPS; otherwise, it blocks them due to mixed content security restrictions.&lt;/p&gt;

&lt;p&gt;To get HTTPS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I needed a domain&lt;/li&gt;
&lt;li&gt;I didn’t want to spend money on a hobby project&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Solution: Render as a Proxy
&lt;/h3&gt;

&lt;p&gt;I reused Render, but differently.&lt;/p&gt;

&lt;p&gt;Instead of hosting the backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I created a &lt;strong&gt;proxy service&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;It forwards requests from a secure Render URL → AWS backend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users hit a secure HTTPS endpoint (Render)&lt;/li&gt;
&lt;li&gt;Requests are forwarded to my AWS server&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Problem 4: Keeping the Backend Updated
&lt;/h2&gt;

&lt;p&gt;After deployment, another issue appeared:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When I push new code, how does AWS get it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Initially, the answer was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH into the instance&lt;/li&gt;
&lt;li&gt;Pull the latest code manually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clearly not scalable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Solution: CI/CD
&lt;/h2&gt;

&lt;p&gt;Time to implement CI/CD (this, too, I only knew in theory before this).&lt;/p&gt;

&lt;h3&gt;
  
  
  What I did:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Wrote a simple &lt;strong&gt;YAML pipeline&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Automated pulling the latest code on deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I still have a lot to learn about Docker and CI/CD.&lt;br&gt;
This setup is basic but functional.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Final Result
&lt;/h2&gt;

&lt;p&gt;Everything is now working end-to-end:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure access via proxy&lt;/li&gt;
&lt;li&gt;Scalable job processing via queues&lt;/li&gt;
&lt;li&gt;Real-time updates via WebSockets&lt;/li&gt;
&lt;li&gt;Safe execution using Puppeteer + isolation&lt;/li&gt;
&lt;li&gt;Automated deployment via CI/CD&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;👉 &lt;a href="https://reactpg.vercel.app" rel="noopener noreferrer"&gt;https://reactpg.vercel.app&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;I’ll be writing another blog soon covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System architecture in depth&lt;/li&gt;
&lt;li&gt;Detailed flow diagrams&lt;/li&gt;
&lt;li&gt;Design decisions and trade-offs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;This project forced me to move from theory → practice in multiple areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System design&lt;/li&gt;
&lt;li&gt;DevOps&lt;/li&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;li&gt;Debugging real-world issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And honestly, that’s where most of the learning happened.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>architecture</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
