A full-stack AI mock interview platform with conversational voice interviewers, resume analysis, real-time transcription, and analytics.
⚠️ Note: Hosted on Render's free tier — expect 30-60s cold starts on first load. TTS, Whisper transcription, and real-time voice features run significantly faster on local deployment. See setup guide below for best experience.
| Service | URL |
|---|---|
| Frontend | https://mockmate-frontend-b8z6.onrender.com |
| Backend API | https://mockmate-backend-rd60.onrender.com |
| AI Service | https://mockmate-ai-k2pt.onrender.com |
| Database | PostgreSQL via Supabase (ap-northeast-1, Tokyo) |
| Cache / Queue | Redis via Upstash (managed, TLS) |
⚠️ Free-tier Supabase projects pause after 7 days of inactivity — if the demo is down, the DB may need a manual wake-up.
Browser (UI)
│ HTTPS / WebSocket
▼
Frontend (Vite + React Router) ← port 3000
│ REST
▼
Backend API (Express + Prisma) ← port 5000
│ SQL │ Redis (BullMQ)
▼ ▼
PostgreSQL Queue jobs
│
▼
Worker process (BullMQ)
│
▼
AI Service (FastAPI + Groq) ← port 8000
├── gTTS (MP3 audio)
├── Faster-Whisper (transcription)
└── Groq LLM (conversation + evaluation)
| Layer | Technology |
|---|---|
| Frontend | Vite, React 19, React Router, TailwindCSS 4, Zustand, shadcn/ui |
| Backend | Node.js, Express 5, TypeScript, Prisma ORM |
| AI Service | Python 3.12, FastAPI, Groq (via OpenAI client) |
| TTS | gTTS (Google Text-to-Speech) |
| Transcription | Faster-Whisper base model (CPU, int8) |
| Database | PostgreSQL 16 |
| Cache / Queue | Redis 7, BullMQ |
| Realtime | Socket.IO (signaling) |
| Auth | JWT, bcrypt, HTTP-only cookies, RBAC |
| DevOps | Docker Compose, GitHub Actions, Prometheus, Grafana |
⚡ Tip: Local deployment runs significantly faster than Render or other free-tier cloud platforms. Render's cold starts and shared CPU noticeably slow down TTS synthesis, Whisper transcription, and real-time WebSocket features. We strongly recommend running locally for the best experience. You can run this project in two ways: Using Docker (Recommended) or Manual Local Setup.
- Node.js v20+
- Python v3.12+
- Docker & Docker Compose (Docker method)
- PostgreSQL 16 + Redis 7 (Manual method)
- A Groq API Key — Get one free at console.groq.com
Spins up all 8 services (Frontend, Backend, Worker, AI Service, Database, Redis, Prometheus, Grafana) with one command.
git clone https://github.com/DSurya11/mockmate.git
cd mockmate# Windows PowerShell
$env:GROQ_API_KEY="your-groq-api-key-here"
docker compose up --build -d
# Mac / Linux
GROQ_API_KEY="your-groq-api-key-here" docker compose up --build -dWait 30-60 seconds for all services to start.
- Frontend UI:
http://localhost:3000 - Backend API:
http://localhost:5000 - AI Service:
http://localhost:8000 - Grafana:
http://localhost:3001(admin / admin) - Prometheus:
http://localhost:9090
Problem: "GROQ_API_KEY not set"
- Solution: Make sure you set the environment variable before running
docker compose up.
Problem: "Port already in use"
- Solution: Stop any services using ports 3000, 5000, 8000, 5432, 6379, 9090, or 3001.
docker compose down # Then restart docker compose up -d
If you prefer to run services individually without Docker.
git clone https://github.com/DSurya11/mockmate.git
cd mockmatedocker compose up postgres redis -dWait 10 seconds for them to be ready.
cd backend
npm installCreate backend/.env file with these values:
PORT=5000
NODE_ENV=development
FRONTEND_URL=http://localhost:3000
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/ai_interview?schema=public"
JWT_SECRET=your-super-secret-jwt-key-change-this
JWT_REFRESH_SECRET=your-refresh-secret-key-change-this
JWT_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
REDIS_URL=redis://localhost:6379
AI_SERVICE_URL=http://localhost:8000
GROQ_API_KEY=your-groq-api-key-here
UPLOAD_DIR=./uploads
MAX_FILE_SIZE=10485760Run database migrations and start:
npx prisma generate
npx prisma db push
npm run devBackend should now be running on http://localhost:5000.
Open a new terminal window, then:
cd ai-service
python -m venv venv
# Activate virtual environment:
# Windows PowerShell:
venv\Scripts\activate
# Mac/Linux:
source venv/bin/activate
pip install -r requirements.txtCreate ai-service/.env file:
AI_SERVICE_PORT=8000
GROQ_API_KEY=your-groq-api-key-here
BACKEND_URL=http://localhost:5000Start the AI service:
python -m app.mainAI service should now be running on http://localhost:8000.
Open a new terminal window, then:
cd frontend
npm installCreate frontend/.env.local file:
VITE_API_URL=http://localhost:5000/api
VITE_WS_URL=http://localhost:5000Start the frontend:
npm run devOpen http://localhost:3000 in your browser.
Problem: "Cannot connect to database"
- Make sure
docker compose up postgres redis -dis running. - Check
DATABASE_URLinbackend/.envmatches the connection string.
Problem: "Module not found" errors
- Make sure you ran
npm installin bothbackend/andfrontend/. - For AI service, make sure you activated the virtual environment before
pip install.
- Conversational AI Interviewers — 3 distinct personas (Alex, Marcus, Sarah), each with a unique voice, tone, and specialty area, powered by Groq LLMs
- 6-Phase Interview Flow — Greeting → Small Talk → Agenda → Background → Core Questions → Closing, enforced by a master prompt
- gTTS Voice Synthesis — Google Text-to-Speech for audio generation; falls back to browser
speechSynthesis - Hybrid Transcription — Browser
SpeechRecognitionprovides live word display during recording; Faster-Whisper (base model) delivers the final accurate transcript after stop - 4-Second Review Window — After Whisper processes the answer, a countdown gives the candidate time to review before auto-submit
- Adaptive Follow-up Questions — Backend injects AI-generated follow-ups based on answers (up to 4, capped at 12 total questions)
- Resume Analysis — PDF upload, ATS scoring, skill extraction via Groq, processed via BullMQ workers
- 6 Interview Types — Technical, Behavioral, System Design, HR, DSA, Mixed
- Analytics Dashboard — Score trends, category breakdowns, recent interview history
- JWT Auth — HTTP-only cookie tokens, refresh token rotation, RBAC (Candidate / Recruiter / Admin)
- Prometheus + Grafana — HTTP request metrics, WebSocket connection gauge, worker job metrics
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register |
Register (CANDIDATE or RECRUITER) |
| POST | /api/auth/login |
Login, sets HTTP-only cookies |
| POST | /api/auth/refresh |
Rotate refresh token |
| GET | /api/auth/me |
Current authenticated user |
| POST | /api/resumes |
Upload PDF resume |
| GET | /api/resumes |
List resumes |
| POST | /api/resumes/:id/analyze |
Trigger AI analysis |
| POST | /api/interviews |
Create interview + generate questions |
| GET | /api/interviews/:id |
Get interview with questions |
| PATCH | /api/interviews/:id/start |
Start interview (→ IN_PROGRESS) |
| PATCH | /api/interviews/:id/complete |
Complete + compute scores |
| POST | /api/interviews/conversational |
AI conversational turn (proxied to AI service) |
| POST | /api/interviews/questions/:id/answer |
Submit answer transcript |
| POST | /api/tts |
Synthesize speech (gTTS → MP3) |
| POST | /api/transcribe/ |
Transcribe audio (Faster-Whisper) |
| GET | /api/analytics/candidate |
Candidate score analytics |
| GET | /api/health |
Health check |
| GET | /metrics |
Prometheus metrics |
├── frontend/ Vite + React 19 + React Router (port 3000)
├── backend/ Express 5 + TypeScript + Prisma (port 5000)
├── ai-service/ FastAPI + Groq + gTTS + Faster-Whisper (port 8000)
├── monitoring/ Prometheus + Grafana configs
├── .github/ CI/CD workflows
└── docker-compose.yml Full 8-service orchestration
The following files are NOT committed to Git because they're too large for GitHub:
*.wav,*.webm— Test audio filestestdata/*.pdf— Sample resume PDFs
These files are automatically ignored via .gitignore.
If you clone this repo and want to contribute:
-
Never commit large binary files. If you add new test audio files, add them to
.gitignore. -
If you accidentally stage a large file:
git rm --cached path/to/large-file git commit --amend --no-edit
-
Push safely — Git will reject pushes with files >100 MB. If that happens, remove them from history before pushing.