A developer community platform — real-time forum chat, a multi-language code playground, leaderboards, and a Q&A space — built on Next.js 15.
Live: https://vibexcode.netlify.app
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 15 (App Router) + React 19 | File-based routing, server components for SEO/metadata, API routes in the same project — one deploy, no CORS |
| Language | TypeScript | The frontend/backend contract is the riskiest surface; type-checking catches it at compile time |
| Styling | Tailwind 4 | Small bundle, no custom CSS, fast iteration |
| State | Redux Toolkit | Centralizes auth and theme state shared across pages |
| Editor | Monaco (@monaco-editor/react) |
Same engine as VS Code, multi-language, familiar to devs |
| Auth | Firebase Auth | Mature social-login flows (Google/GitHub/Facebook) and email/password without me handling tokens or password hashing |
| DB | MongoDB Atlas + Mongoose | Document model fits chat messages, submissions, and user profiles; Mongoose layers schemas + validation on top |
| Code execution | Judge0 (RapidAPI) | Sandboxes user code in containers — submissions never run on our infrastructure |
| Real-time | Socket.IO (dev) / pluggable | Rooms keyed by conversation ID; messages also persisted to Mongo so refresh shows history (see "Known issues" for the deploy-time caveat) |
| Hosting | Netlify | Static + serverless functions, generous free tier |
The features are conventional. The decisions inside them are where the depth lives.
The playground compiles user code in Judge0, an external sandbox. Flow:
- Client
POST /api/judge0/submitwith source + language ID - Our route forwards to RapidAPI's Judge0 endpoint, returns a
token - Client polls
/api/judge0/result/<token>untilstatus.id > 2(completed) - UI renders stdout / stderr / time / memory
If a user submits malicious code, the blast radius is their own Judge0 container — not our app. The trade-off is network latency (typically 1–4 s) and an external dependency. The polling is a known limitation; webhook callback would be better and is a planned change.
Each conversation has an ID. On open, the client joins a Socket.IO room keyed by that ID. New messages emit only to that room and are persisted to the Message collection in Mongo, so a refresh re-fetches history without missing anything sent in-flight. Real-time without persistence is amnesia; persistence without real-time is polling. You need both.
Firebase handles auth end-to-end. An earlier version of this codebase ran Appwrite + Firebase in parallel, which was a real production wart (Appwrite Cloud's free tier auto-pauses after a week of inactivity → red 403s on every page that called authservice.checkUser()). The current code uses Firebase only; the legacy authservice interface is preserved as a Firebase-backed shim so the consolidation didn't ripple through every page.
Root layout exports rich Open Graph + Twitter card metadata + canonical URL, with a per-route title template (Playground · VibeXcode). Each major route has its own minimal layout.tsx that overrides title/description so link previews and search results are page-accurate, not site-default.
.
├── app/
│ ├── layout.tsx # Root layout, full SEO metadata
│ ├── page.tsx # Landing
│ ├── api/ # Next.js API routes (REST)
│ │ ├── judge0/ # Submit + poll for code execution
│ │ ├── messages/ # Chat history
│ │ ├── questions/ # Q&A questions
│ │ ├── submit/ # Solution submissions
│ │ ├── tasks/ # Personal todos
│ │ └── ...
│ ├── playground/ # Monaco editor + Judge0 flow
│ ├── community/, Forums/ # Real-time chat
│ ├── Dashboard/, Profile/ # User-scoped views
│ ├── Leaderboards/ # Top performers
│ ├── login/, signup/ # Auth flows (Firebase)
│ ├── appwrite/auth.ts # Firebase-backed auth service (legacy folder name)
│ └── components/ # Shared UI
├── lib/
│ ├── firebase.ts # Firebase client init
│ ├── mongodb.ts # Lazy Mongoose connection
│ ├── judge0.ts # Submit + poll wrapper
│ └── useSocket.ts # Socket.IO React hook
├── models/ # Mongoose schemas
│ ├── Users.ts
│ ├── Messages.ts
│ ├── Questions.ts
│ ├── Submissions.ts
│ └── Tasks.ts
├── pages/api/socketio.ts # Pages-router Socket.IO server (legacy; see "Known issues")
└── public/
Requirements: Node ≥ 18.
git clone https://github.com/Valkyriezz/VibexCode.git
cd VibexCode
npm install
cp .env.example .env.local # then fill in the values
npm run dev # http://localhost:3000.env.local needs (see .env.example for the full list):
MONGODB_URI— MongoDB Atlas or localRAPIDAPI_KEY— Judge0 (Run button fails with 401 without this)- Firebase web SDK config (
NEXT_PUBLIC_FIREBASE_*)
Build the production bundle and serve it:
npm run build
npm startThe app is deploy-target-agnostic. Configure the env vars above on the host and npm run build works. Netlify is the current target (auto-builds on push to main, ~2 min).
- Real-time chat on serverless.
pages/api/socketio.tsrequires a long-lived process. Netlify Functions are short-lived, so the deployed/api/socketioroute returns 500. Local dev is fine. Fix is to migrate to a managed real-time service (Pusher / Ably) or move the host to Render/Railway. - Judge0 polling. Polling every second up to 10 attempts is brittle for slow programs. Webhook callback is the right answer — Judge0 supports it on paid tiers.
- Reset password URL. With Firebase, the reset email carries
oobCodeinstead of Appwrite'suserId+secret. The/resetPasswordpage still reads the legacy params; needs updating. - No automatic DB migrations. Mongo schemas are defined inline; for v2 a
_migrationscollection + numbered scripts would be the right shape. - No rate limiting. API routes are open. Realistic to add via
@vercel/edgeor middleware before scale. - No tests for the chat layer. API route tests for Judge0 are planned.
- A heavyweight design system (MUI etc.). Tailwind keeps the bundle small and review attention on the logic.
- Per-tenant auth / multi-org. Single-tenant by design; a real product would need this.
- A separate frontend host. One URL is simpler to operate; we can split later if traffic justifies it.
- Premature abstractions (repository pattern, service layer, DI containers). At this size they cost more than they save.
The brief I built this against valued depth and production-readiness over breadth of features. The visible features (chat, playground, forum, leaderboard) are conventional. The interesting parts are the seams: how user code is sandboxed, how auth was consolidated, how chat persistence and real-time delivery share the same data path, how metadata is structured. Where I cut a corner I tried to leave a clean seam (Known issues above) so the next iteration is small.