Your AI-powered interview preparation workspace
| Service | URL |
|---|---|
| Frontend | prepiqfrontend.vercel.app |
| Backend API | prepiq-backend-c79d.onrender.com |
| API Docs | /docs |
| Health Check | /api/health |
| Database | Neon Postgres (managed) |
Note: The backend runs on Render's free tier and may take ~30 seconds to wake up on first request after inactivity.
PrepIQ is a full-stack interview preparation platform that combines career profiling, AI-assisted prep plans, mock interviews with scoring, job application tracking, and progress analytics — all in one workspace.
- Account Management — Signup, login, and persistent sessions with secure token auth
- Career DNA Profiling — Multi-step onboarding to capture skills, experience, and goals
- AI Interview Prep — Auto-generated gap analysis, question banks, and study roadmaps via OpenRouter
- Mock Interviews — Practice with AI-scored answers, feedback, and model responses
- Job Application Tracker — Kanban and table views with status, contacts, and next actions
- Progress Dashboard — Visual analytics for prep sessions, scores, and activity trends
- Local ML/NLP — spaCy NER skill extraction, TF-IDF resume–JD matching, TextBlob confidence scoring — all without an API key
Dashboard — personalized welcome, stats overview, and quick actions
Login — clean dark-themed authentication interface
| Layer | Technologies |
|---|---|
| Frontend | React 18, TypeScript, Vite, Tailwind CSS, shadcn/ui, Radix UI, Framer Motion, Recharts |
| Backend | FastAPI, SQLAlchemy, Pydantic v2, Uvicorn |
| Database | PostgreSQL (Neon) · SQLite for local dev and tests |
| AI | OpenRouter (free models with graceful mock fallback) |
| ML / NLP | spaCy 3.7 (NER), scikit-learn (TF-IDF cosine similarity), TextBlob (sentiment analysis) |
| Auth | HMAC-signed bearer tokens, PBKDF2 password hashing |
| Deployment | Vercel (frontend) · Render (backend) · Neon (database) |
| Tooling | ESLint, Vitest, Playwright, Docker, GitHub Actions |
- Node.js 22+
- Python 3.10-3.12 recommended
- PostgreSQL 16+ for Docker/production, or SQLite for simple local development
The backend Docker image uses Python 3.10, and CI currently verifies Python 3.10 and 3.11. Newer Python versions may not have compatible wheels for the pinned ML/NLP dependencies.
git clone https://github.com/Aashikhandelwal05/prepiq.git
cd prepiqCopy the example environment file:
# macOS/Linux
cp .env.example .env# Windows PowerShell
Copy-Item .env.example .envOpen .env and update the values for your local setup.
For simple manual local development, use SQLite instead of the Docker-oriented PostgreSQL default:
DATABASE_URL=sqlite:///./backend/local.db
APP_SECRET=any-long-random-string-only-for-local-dev
⚠️ Never commit a realAPP_SECRET. It signs authentication tokens, so treat it like a password.
For local frontend development, keep VITE_API_BASE_URL blank:
VITE_API_BASE_URL=The Vite dev server automatically proxies /api requests to localhost:8000.
Only set VITE_API_BASE_URL when the frontend is deployed separately, such as a Vercel frontend pointing to a Render backend URL.
# Frontend
npm installCreate and activate a Python virtual environment before installing backend dependencies:
# macOS/Linux
python -m venv .venv
source .venv/bin/activate
python -m pip install -r backend/requirements.txt
# NLP model assets — required for ML features
python -m spacy download en_core_web_sm
python -m textblob.download_corpora# Windows PowerShell
py -3.11 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r backend/requirements.txt
# NLP model assets — required for ML features
python -m spacy download en_core_web_sm
python -m textblob.download_corporaIf you use Python 3.10 or 3.12 instead, replace 3.11 with your installed supported version.
If you skip the NLP step, the app still works — ML features fall back to keyword-only matching and neutral sentiment scores.
Run the backend from the activated virtual environment:
# Terminal 1 — Backend (port 8000)
python -m uvicorn backend.app.main:app --reload --host 127.0.0.1 --port 8000Run the frontend in a separate terminal:
# Terminal 2 — Frontend (port 8080)
npm run devOpen http://localhost:8080 in your browser.
When we run both servers locally, we have two processes running on different ports:
| Process | Command | Port |
|---|---|---|
| Frontend (Vite dev server) | npm run dev |
8080 |
| Backend (FastAPI / Uvicorn) | python -m uvicorn ... |
8000 |
Normally, browsers block JavaScript from making requests to a different port — a browser security rule called CORS. Vite solves this with a built-in reverse proxy: one can think of it like a silent traffic redirector sitting inside the dev server. One does not need to install or configure anything extra — it is already set up.
The proxy rule lives in vite.config.ts:
proxy: {
"/api": {
target: "http://127.0.0.1:8000",
changeOrigin: true,
},
},Any request the React code makes to a path starting with /api is automatically forwarded by Vite to http://127.0.0.1:8000. FastAPI receives it as a normal request — it never knows that the browser was on a different port.
Browser (localhost:8080)
│
│ fetch("/api/auth/me") ← relative URL, no hostname
▼
Vite dev server (port 8080)
│
│ proxy rule: /api → 127.0.0.1:8000
▼
FastAPI (port 8000) ← receives and handles the request
Why
VITE_API_BASE_URLmust be blank for local developmentThe fetch wrapper in
src/lib/api.tsprependsVITE_API_BASE_URLto every API call before sending it.
When it is blank, the URL stays relative:
/api/auth/meThe browser hands it to Vite → Vite proxies it → FastAPI on port 8000 handles it. ✅When it is set to
http://127.0.0.1:8000, the URL becomes absolute:http://127.0.0.1:8000/api/auth/meThe browser sends it directly, completely bypassing Vite's proxy. The browser's CORS rules then block it, and every API call fails with a network error. ❌Bottom line: leave
VITE_API_BASE_URLcompletely empty in one's.envfor local development.
When we deploy, there is no Vite dev server running — only a pre-built bundle of static files served by a CDN. At build time, Vite permanently replaces every reference to import.meta.env.VITE_API_BASE_URL in the code with the actual value we provided. This means every API call in the deployed app becomes an absolute URL that the browser sends directly to the hosted backend, with no proxy involved:
Browser (prepiqfrontend.vercel.app)
│
│ fetch("https://prepiq-backend-c79d.onrender.com/api/auth/me")
│ ↑ full URL baked in at build time from VITE_API_BASE_URL
▼
Render — FastAPI ← browser calls it directly, no proxy
Set VITE_API_BASE_URL in the hosting provider's environment variables (Vercel → Project Settings → Environment Variables) to the full Render backend URL. Do not add a trailing slash.
VITE_API_BASE_URL=https://prepiq-backend-c79d.onrender.com
Note: Because
VITE_API_BASE_URLis substituted at build time (not runtime), one must trigger a new Vercel deployment after changing it for the new value to take effect.
| Scenario | VITE_API_BASE_URL value |
How requests reach FastAPI |
|---|---|---|
| Local development | (leave blank) | Vite proxy: port 8080 → port 8000 |
| Deployed to Vercel | https://prepiq-backend-c79d.onrender.com |
Browser → Render directly |
| Custom deployment | https://api.yourdomain.com |
Browser → one's backend directly |
Runs PostgreSQL, backend, and frontend (served via nginx) together:
docker compose up --build| Variable | Description | Required |
|---|---|---|
DATABASE_URL |
Database connection string used by the backend. Use PostgreSQL for Docker/production or SQLite for simple local development. | ✅ |
APP_SECRET |
Long random secret used for signing authentication tokens. Never commit a real value. | ✅ |
CORS_ORIGINS |
Comma-separated list of frontend URLs allowed to access the backend API. | ✅ |
ACCESS_TOKEN_TTL_HOURS |
Token expiry time in hours. Default value is 168. |
❌ |
OPENROUTER_API_KEY |
OpenRouter API key. Leave blank to use the built-in mock fallback. | ❌ |
OPENROUTER_MODEL |
OpenRouter model name. Default value is nvidia/nemotron-3-super-120b-a12b:free. |
❌ |
OPENROUTER_APP_URL |
App URL sent to OpenRouter. | ❌ |
OPENROUTER_APP_NAME |
App name shown in OpenRouter. | ❌ |
OPENROUTER_TIMEOUT_SECONDS |
OpenRouter request timeout in seconds. Default value is 30. |
❌ |
VITE_API_BASE_URL |
Backend API URL for deployed frontend builds. Leave blank for local development. | ❌ |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/auth/signup |
Create a new account |
POST |
/api/auth/login |
Login with email & password |
GET |
/api/auth/me |
Get current user from token |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/users/{id}/profile |
Get career DNA profile |
PUT |
/api/users/{id}/profile |
Save/update profile |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/users/{id}/sessions |
List all prep sessions |
GET |
/api/users/{id}/sessions/{sid} |
Get session details |
POST |
/api/users/{id}/sessions |
Create new AI prep session |
DELETE |
/api/users/{id}/sessions/{sid} |
Delete a prep session |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/users/{id}/mocks |
List all mock attempts |
POST |
/api/users/{id}/mocks |
Submit answer for AI scoring |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/users/{id}/jobs |
List all job applications |
POST |
/api/users/{id}/jobs |
Add a new application |
PATCH |
/api/users/{id}/jobs/{jid} |
Update application details |
DELETE |
/api/users/{id}/jobs/{jid} |
Delete an application |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/ml/extract-skills |
Extract skills from text using spaCy NER + keyword matching |
POST |
/api/ml/match-score |
TF-IDF cosine similarity score between resume and job description |
POST |
/api/ml/analyze-confidence |
TextBlob sentiment, specificity, and word count analysis |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/health |
Health check |
Full interactive API docs:
/docs
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI app — models, routes, AI logic
│ │ └── ml.py # Local ML module: spaCy NER, TF-IDF, TextBlob
│ ├── tests/
│ │ └── test_api.py # Backend API smoke tests
│ └── requirements.txt # Python dependencies
│
├── src/
│ ├── components/
│ │ ├── ui/ # 49 shadcn/ui components
│ │ ├── AppLayout.tsx # Main layout shell
│ │ ├── AppSidebar.tsx # Navigation sidebar
│ │ └── ScoreCircle.tsx # Animated SVG score display
│ ├── pages/
│ │ ├── AuthPage.tsx # Login / Signup
│ │ ├── DashboardPage.tsx # Main dashboard with stats
│ │ ├── OnboardingPage.tsx # Career DNA multi-step wizard
│ │ ├── InterviewPrepPage.tsx# AI prep session view
│ │ ├── MockInterviewPage.tsx# Mock interview + ML confidence analysis
│ │ ├── JobTrackerPage.tsx # Kanban & table job tracker
│ │ ├── ProgressPage.tsx # Analytics & progress charts
│ │ └── CareerDNAPage.tsx # Profile viewer/editor
│ ├── lib/
│ │ ├── api.ts # Fetch wrapper with auth token injection
│ │ ├── store.ts # Global state and data-fetching hooks
│ │ └── utils.ts # cn() and other utilities
│ └── hooks/ # use-toast, use-mobile
│
├── docs/ # Screenshots and open-source docs
├── Dockerfile.backend # Backend Docker image
├── Dockerfile.frontend # Frontend Docker image (nginx)
├── docker-compose.yml # Full-stack local setup
├── vercel.json # Vercel SPA routing config
├── vite.config.ts # Vite build + API proxy config
└── package.json # Frontend deps & scripts
Contributions are welcome — bug fixes, features, docs improvements, and tests all count.
- Read CONTRIBUTING.md for the full guide
- Browse open issues — look for
good first issueandhelp wantedlabels - Comment on an issue before starting so it can be assigned to you
# Frontend
npm run lint && npm test && npm run build
# Backend
python -m compileall backend
python -m unittest discover -s backend/tests -p "test_*.py"| Service | Platform | Purpose |
|---|---|---|
| Frontend | Vercel | Static React build served via nginx |
| Backend | Render | Dockerized FastAPI |
| Database | Neon | Managed PostgreSQL |
- Neon — Create a free project, copy the connection string
- Render — New Web Service → Docker →
Dockerfile.backend→ set env vars - Vercel — Import repo → Vite preset → set
VITE_API_BASE_URLto your Render URL - CORS — Update
CORS_ORIGINSon Render with your Vercel domain
- Resume upload and PDF parsing
- Multiple AI model selection in the UI
- Interview session sharing and collaboration
- Email notifications for job application updates
- Mobile-responsive PWA support
- Migrate data fetching to TanStack Query (already installed)
- End-to-end Playwright tests for auth and dashboard flows
Want to pick something up? Open an issue or comment on an existing one.
This project is licensed under the MIT License — see the LICENSE file for details.
Built with ❤️ by Aashi Khandelwal and contributors

