The application is built on a distributed microservices architecture designed to handle high concurrency and isolate the risk of code execution.
- Frontend (Client): A React-based SPA built with Vite. It features a rich code editor (Monaco) and a dashboard for users to track their progress and submission history.
- Backend (API Gateway): An Express/Node.js server that handles authentication, database operations, and orchestrates code submissions.
- Database: PostgreSQL (via Prisma ORM) stores user profiles, settings, and submission records.
- Task Queue: A Redis List (
submissions) is used to queue incoming code executions to prevent the backend from blocking or crashing due to heavy computation. - Worker Node(s): A separate Node.js process that continuously pops tasks from the Redis queue. It uses
child_process.spawnto execute the user's code in a secure environment. - Pub/Sub Broker: A Redis Pub/Sub channel (
worker-response) where workers publish the final output (or error) of the executed code. - Real-time Streaming: The backend subscribes to the Pub/Sub channel and routes the execution results back to the original client using Server-Sent Events (SSE).
When building the real-time execution feedback loop, SSE was deliberately chosen over alternatives:
- WebSockets: Requires maintaining a persistent, full-duplex connection for every active client. This consumes a significant amount of memory on the server and is notoriously difficult to scale under a large number of concurrent users.
- Long Polling: Can simulate real-time updates but increases overall coding complexity and introduces unnecessary HTTP overhead and latency due to repeated request-response cycles.
- SSE (Server-Sent Events): Is the optimal choice because the communication pattern here is strictly unidirectional (server pushing results to the client). SSE allows the server to transmit data at any time with minimal implementation complexity and drastically lower memory footprint compared to WebSockets.
- Framework: React 19, TypeScript, Vite
- Styling: Tailwind CSS v4, Lucide React (Icons), Shadcn UI components
- State & Data Fetching: React Query, Zustand
- Routing: React Router
- Editor: Monaco Editor (
monaco-editor)
- Server: Node.js, Express (TypeScript)
- Database / ORM: PostgreSQL, Prisma
- Authentication: Better Auth (
better-auth) - Queue & Pub/Sub: Redis (
redisclient)
- Runtime: Node.js (TypeScript)
- Execution Engine:
node:child_process(spawnspython3ornodedepending on the language selected) - Broker: Redis
beatcode/
βββ backend/ # Express API Server
β βββ index.ts # Main server, SSE endpoint, API routes
β βββ lib/ # Authentication (Better Auth) and Prisma clients
β βββ prisma/ # Database schema and migrations
β βββ subscriber/ # Redis Pub/Sub listener routing messages to SSE
βββ frontend/ # React UI Client
β βββ src/
β β βββ components/ # Reusable UI components (Shadcn, etc.)
β β βββ lib/ # API hooks, utilities, auth clients
β β βββ pages/ # Application routes (Playground, Settings, Dashboard, etc.)
β β βββ App.tsx # React Router configuration
βββ worker/ # Code Execution Engine
β βββ index.ts # Dequeues tasks, spawns processes, publishes results
β βββ config/ # Redis connection setup
βββ README.md # You are here
- Write Code: The user types code into the Monaco Editor on the
PlaygroundPageand clicks "Run". - Submit: A POST request is sent to the backend
/submitendpoint. - Persist & Queue: The backend creates a
Pendingrecord in PostgreSQL (via Prisma) and pushes the raw code, language, and submission ID into the Redissubmissionslist. - Wait for SSE: The frontend establishes an SSE connection to
/eventsand waits for a server push. - Execute: The
workerpops the item from the Redis list. It spawns a child process (e.g.,python3ornode) and pipes the code into the process's standard input. - Capture: The worker listens to
stdoutandstderrstreams of the spawned process, capturing the output or compilation errors. - Publish: The worker publishes a JSON payload containing the output to the Redis
worker-responsePub/Sub channel. - Relay: The backend's
listener.tscatches the Pub/Sub event, updates the PostgreSQL record toSuccess/Errorwith the output, and writes the data to the specific user's open SSE stream. - Display: The frontend receives the SSE payload, closes the loading state, and renders the output in the terminal panel.
To ensure the safety and stability of the platform, the worker node implements several protective measures during code execution:
- Malicious Code Prevention: Before execution, the code is analyzed using regular expressions (
isMaliciouscheck) to block dangerous modules and functions.- Python: Blocks
os,sys,subprocess,shutil,importlib,builtins,socket,eval,exec,open, and__import__. - Node.js: Blocks
require,import ... from,child_process,fs,process,eval, andFunction. Submissions violating these rules immediately fail with a "Security Error".
- Python: Blocks
- Execution Timeout: A strict 5-second (5000ms) time limit is enforced on all code executions. If a process exceeds this limit (e.g., due to an infinite loop), it is forcefully terminated (
SIGKILL), and a "Time Limit Exceeded" error is returned. - Reliable Result Delivery: The worker publishes the execution results (output, error, and exit code) to the
worker-responseRedis channel. It features a retry mechanism (up to 3 times) if the initial publish operation fails.
- Node.js & Bun
- PostgreSQL (Running locally or via Docker)
- Redis (Running locally or via Docker)
- Python (If you wish to execute Python code locally)
- Clone the repository
- Install dependencies in all workspaces:
cd backend && bun install cd ../frontend && npm install cd ../worker && bun install
- Environment Variables: Set up your
.envfiles inbackend/andworker/with yourDATABASE_URLandREDIS_URL. - Database Migration:
cd backend npx prisma migrate dev
Backend (backend/.env)
REDIS_URL="..." # Upstash or local Redis URL
DATABASE_URL="..." # PostgreSQL connection string
BETTER_AUTH_SECRET="..." # Random secret for auth
BETTER_AUTH_URL="http://localhost:3000"
GOOGLE_CLIENT_ID="..." # Google OAuth Client ID
GOOGLE_CLIENT_SECRET="..." # Google OAuth Client SecretWorker (worker/.env)
REDIS_URL="..." # Must match the backend's REDIS_URLYou need to start all three services simultaneously:
# Terminal 1: Backend
cd backend && bun run dev
# Terminal 2: Worker
cd worker && bun run dev
# Terminal 3: Frontend
cd frontend && npm run devPOST /submit: Accepts{ language, code, userId }. Pushes the task to Redis and returns asubmissionId.GET /events: Establishes a Server-Sent Events (SSE) connection to stream real-time output updates.
GET /api/dashboard: Returns aggregate user stats (total runs, languages used) and recent submissions.GET /api/daily: Fetches the curated code snippet of the day and recent successful runs for the user.GET /api/submissions: Lists the user's past code submissions with pagination and filters (?page=1&limit=20).GET /api/submissions/:id: Fetches details for a specific code submission.
GET /api/profile: Retrieves user profile data and computed statistics (e.g., GitHub-style heatmap data).PUT /api/profile: Updates the user's profile information.PUT /api/settings: Updates user application settings.