Create any dev challenge. Log your progress daily. Share your streak publicly.
Live Demo → devstrex.vercel.app
DevStrex is a full stack MERN application for developers who run structured learning challenges.
Create a challenge, define your daily tasks, log your progress every day, and share your public profile with one link — no login required to view it.
Built during a 30-day MERN challenge. Used to track the same 30-day challenge while building it.
- Create Challenges — define a title, description, total days, and daily tasks
- Daily Logging — log each day with a note and completion status
- Streak Tracking — automatically calculates your current streak
- Progress Heatmap — visual map of every completed day
- Public Profiles — shareable
/usernamepage, no login needed to view - JWT Authentication — secure signup and login
- Protected Routes — dashboard and challenge pages require authentication
- Fully Typed — TypeScript across the entire stack
| Technology | Purpose |
|---|---|
| Node.js | Runtime |
| Express | REST API framework |
| TypeScript | Type safety |
| MongoDB | Database |
| Mongoose | ODM |
| bcrypt | Password hashing |
| jsonwebtoken | JWT authentication |
| dotenv | Environment variables |
| cors | Cross-origin requests |
| Technology | Purpose |
|---|---|
| React 18 | UI framework |
| Vite | Build tool |
| TypeScript | Type safety |
| Tailwind v4 | Styling |
| shadcn/ui + Radix | Component library |
| Framer Motion | Animations |
| React Router v6 | Client-side routing |
| Lucide React | Icons |
| Vercel Analytics | Usage tracking |
| Service | Purpose |
|---|---|
| Render | Backend hosting |
| Vercel | Frontend hosting |
| MongoDB Atlas | Database hosting |
devstrex/
├── backend/
│ ├── src/
│ │ ├── app.ts
│ │ ├── server.ts
│ │ ├── db.ts
│ │ ├── seed.ts
│ │ ├── middleware/
│ │ │ └── authMiddleware.ts
│ │ ├── models/
│ │ │ ├── User.ts
│ │ │ ├── Challenge.ts
│ │ │ └── Log.ts
│ │ └── routes/
│ │ ├── authRoutes.ts
│ │ ├── challengeRoutes.ts
│ │ ├── logRoutes.ts
│ │ └── publicRoutes.ts
│ ├── .env.example
│ ├── package.json
│ └── tsconfig.json
│
└── frontend/
├── src/
│ ├── main.tsx
│ ├── App.tsx
│ ├── index.css
│ ├── lib/
│ │ └── utils.ts
│ ├── types/
│ │ └── index.ts
│ ├── services/
│ │ ├── authService.ts
│ │ └── challengeService.ts
│ ├── components/
│ │ ├── ui/
│ │ │ ├── Spinner.tsx
│ │ │ └── Badge.tsx
│ │ ├── layout/
│ │ │ └── Navbar.tsx
│ │ ├── ChallengeCard.tsx
│ │ └── HeatMap.tsx
│ └── pages/
│ ├── AuthPage.tsx
│ ├── DashboardPage.tsx
│ ├── NewChallengePage.tsx
│ ├── ChallengePage.tsx
│ └── PublicProfilePage.tsx
├── index.html
├── vercel.json
├── vite.config.ts
└── package.json
- Node.js 20+
- MongoDB Atlas account
- Git
git clone https://github.com/yourusername/devstrex.git
cd devstrexcd backend
npm installCreate a .env file:
MONGO_URI=mongodb+srv://<username>:<password>@cluster.mongodb.net/devstrex-db
JWT_SECRET=your_super_secret_key
PORT=3000Start the development server:
npm run devSeed the database with dummy data:
npm run seedcd frontend
npm installCreate a .env file:
VITE_API_URL=http://localhost:3000Start the development server:
npm run devFrontend runs on http://localhost:5173 Backend runs on http://localhost:3000
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /auth/signup |
Register a new user | No |
| POST | /auth/login |
Login and receive JWT | No |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /challenges |
Get all user challenges | Yes |
| GET | /challenges/:id |
Get one challenge with logs | Yes |
| POST | /challenges |
Create a new challenge | Yes |
| PUT | /challenges/:id |
Update a challenge | Yes |
| DELETE | /challenges/:id |
Delete a challenge and its logs | Yes |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /logs/:challengeId |
Get all logs for a challenge | Yes |
| POST | /logs |
Log a day | Yes |
| PUT | /logs/:id |
Update a log | Yes |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /public/:username |
Get public profile with challenges and logs | No |
{
email: string; // unique, required
username: string; // unique, required, 3-20 chars
password: string; // hashed with bcrypt
createdAt: Date;
updatedAt: Date;
}{
userId: ObjectId // ref: User
title: string // required, max 100 chars
description: string // max 500 chars
totalDays: number // 1-365
startDate: Date
tasks: [{
day: number
title: string
}]
isPublic: boolean // default: true
createdAt: Date
updatedAt: Date
}{
userId: ObjectId; // ref: User
challengeId: ObjectId; // ref: Challenge
day: number; // unique per challenge
note: string; // max 1000 chars
completed: boolean; // default: true
createdAt: Date;
updatedAt: Date;
}Build Command: npm install && npm run build
Start Command: node dist/server.js
Environment variables:
MONGO_URI
JWT_SECRET
PORT
Build Command: npm run build
Output Directory: dist
Environment variables:
VITE_API_URL → your Render backend URL
The vercel.json rewrite rule handles client-side routing:
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}Email: alex@devstrex.io
Password: password123
Public profile: devstrex.vercel.app/alex
MONGO_URI=mongodb+srv://<username>:<password>@cluster.mongodb.net/devstrex-db
JWT_SECRET=your_super_secret_key
PORT=3000VITE_API_URL=http://localhost:3000npm run dev # Start development server
npm run build # Compile TypeScript to dist/
npm run start # Run compiled server
npm run seed # Seed database with dummy datanpm run dev # Start Vite development server
npm run build # Build for production
npm run preview # Preview production build- Never pass
req.bodydirectly tofindByIdAndUpdate— destructure only allowed fields - TypeScript strict mode catches bugs before runtime — always enable it
- Seed your database before building UI — empty state is painful to develop against
- Get the data model right before writing routes — changing schemas mid-build is expensive
- Vercel needs a
vercel.jsonrewrite rule for React Router — SPAs need it - Environment variables are baked in at Vite build time — always redeploy after adding them
import * as jwt from "jsonwebtoken"— CommonJS modules have no default export in strict TS
This project was built during a 30-Day MERN Challenge — one day at a time, documented publicly on Twitter.
The meta story: DevStrex was used to track the same challenge it was built during.
This project is currently source-available but not open source.
Source code is publicly visible for portfolio and educational purposes only. Rebranding, redistribution, reselling, and derivative products are strictly prohibited.
Open source release planned for a future date.
See LICENSE for full details.
Built with 🔥 by Rahul Harihar
If DevStrex helped you — give it a ⭐ on GitHub