Parse resumes in bulk. Score candidates against jobs with transparent, explainable logic. Manage your entire hiring pipeline โ in one platform.
Recruiting teams routinely receive hundreds of resumes per role and spend hours manually screening, comparing, and shortlisting candidates. AI Hiring Job automates the heavy lifting of that workflow with a rules-based, fully explainable scoring engine โ no black-box AI, no unexplainable rankings.
- Manual resume screening doesn't scale past a handful of applicants.
- Recruiters lack a consistent, repeatable way to compare candidates against a role's requirements.
- Hiring managers need evidence, not just a score, to justify a shortlist decision.
- Disconnected spreadsheets and email threads make pipeline tracking unreliable.
AI Hiring Job centralizes the entire hiring lifecycle โ job creation, bulk resume ingestion, structured parsing, weighted candidate-to-job matching, pipeline tracking, and analytics โ behind a single authenticated React dashboard backed by a hardened Express + MySQL API.
- Resume Parsing Engine โ extracts skills, experience, and education from PDF/DOCX resumes using
pdf-parseandmammoth, normalized against a curated skills taxonomy. - Explainable Matching Engine โ deterministic, weighted scoring (Required Skills 50% ยท Nice-to-have 20% ยท Experience 20% ยท Education 10%) with a full score breakdown returned per candidate โ every score is traceable to its inputs.
- Duplicate Detection โ content-hash based de-duplication prevents the same resume being scored twice for a role.
- Interview Question Generator โ produces role- and skill-tailored behavioral/technical question banks per candidate-job pair.
- Operational Analytics โ dashboard and per-job funnel metrics for data-driven hiring decisions.
A startup HR team posts a "Senior Backend Engineer" role, bulk-uploads 150 resumes received via email, and within seconds gets a ranked shortlist with a transparent breakdown of why each candidate ranked where they did โ then moves top candidates through a pipeline (Screening โ Interview โ Offer) directly in the app.
- Stateless Express API designed for horizontal scaling โ see the included Kubernetes manifests with 2-replica Deployments, liveness/readiness probes, and an Ingress with TLS.
- File uploads persisted to a dedicated
ReadWriteManyPVC so any pod replica can serve uploaded resumes. - Rate limiting (
express-rate-limit) isolates auth endpoints (20 req/15min) from general API traffic (200 req/15min) to absorb brute-force attempts without degrading normal usage. - MySQL schema is fully normalized with foreign keys and targeted indexes (
idx_applications_job_id,idx_resumes_user_id, etc.) to keep matching queries performant as data grows.
| Feature | Description |
|---|---|
| ๐ JWT + OTP Authentication | Email/password registration with OTP email verification, short-lived access tokens + refresh tokens, secure logout/session invalidation |
| ๐ Bulk Resume Upload | Upload up to 20 resumes at once (PDF/DOCX), parsed automatically into structured candidate profiles |
| ๐งฎ Explainable Matching Engine | Transparent, weighted scoring against job requirements with a full breakdown โ not a black box |
| ๐งฌ Duplicate Resume Detection | SHA-based content hashing flags duplicate submissions per recruiter account |
| ๐ AI-Style Job Description Generator | Auto-drafts a structured job description from title, skills, and experience range |
| ๐๏ธ Candidate Pipeline Tracking | Move candidates through configurable pipeline stages (screening โ interview โ offer) |
| ๐ฏ Interview Question Generator | Generates behavioral & role-specific technical questions per candidate-job pairing |
| ๐ Recruiter Analytics Dashboard | Visual funnel metrics, per-job analytics, and hiring KPIs via Recharts |
| ๐ In-App Notifications | Real-time-style notification feed with unread tracking |
| ๐ก๏ธ Hardened API Surface | Helmet, CORS, rate limiting, centralized error handling, structured Winston logging |
| ๐ณ Container-Native | Multi-stage Dockerfiles, Docker Compose orchestration with health checks |
| โธ๏ธ Kubernetes-Ready | Deployments, Services, ConfigMap/Secret, PVC, and Ingress manifests included |
flowchart TD
User["๐ค Recruiter / Hiring Manager"] -->|HTTPS| FE["โ๏ธ React 18 + Vite SPA<br/>(Zustand ยท Recharts ยท Axios)"]
FE -->|REST /api/*| GW["๐ Nginx Reverse Proxy<br/>(Frontend Container)"]
GW --> API["๐ Express.js API<br/>(Helmet ยท CORS ยท Rate Limiting)"]
API --> AUTH["๐ Auth Controller<br/>JWT + OTP + bcrypt"]
API --> JOBS["๐ผ Job Controller"]
API --> RES["๐ Resume Controller"]
API --> CAND["๐งโ๐ป Candidate Controller"]
API --> ANA["๐ Analytics Controller"]
API --> NOTIF["๐ Notification Controller"]
RES --> PARSE["๐ง Resume Parser Service<br/>(pdf-parse ยท mammoth)"]
CAND --> MATCH["๐ฏ Matching Service<br/>(weighted scoring engine)"]
JOBS --> JDGEN["๐ Job Description Service"]
CAND --> INTV["โ Interview Question Service"]
AUTH --> DB[("๐๏ธ MySQL 8.0<br/>users ยท jobs ยท candidates<br/>resumes ยท applications")]
JOBS --> DB
RES --> DB
CAND --> DB
ANA --> DB
NOTIF --> DB
AUTH -.->|OTP email| SMTP["๐ง SMTP (Gmail)"]
RES -.->|stores files| FS[("๐ Uploads Volume")]
style FE fill:#61DAFB,color:#000
style API fill:#000,color:#fff
style DB fill:#4479A1,color:#fff
flowchart LR
A[Register Account] --> B[Verify OTP via Email]
B --> C[Login โ JWT Issued]
C --> D[Create Job Posting]
D --> E[Generate Job Description]
D --> F[Bulk Upload Resumes]
F --> G[Resumes Auto-Parsed]
G --> H[Run Candidate Matching]
H --> I[Review Ranked Shortlist]
I --> J[Move Candidates Through Pipeline]
J --> K[Generate Interview Questions]
K --> L[View Analytics Dashboard]
sequenceDiagram
participant U as Recruiter (Browser)
participant FE as React SPA
participant API as Express API
participant MW as Auth Middleware
participant Parser as Resume Parser Service
participant Match as Matching Service
participant DB as MySQL
U->>FE: Selects resumes + clicks Upload
FE->>API: POST /api/resumes/upload (multipart)
API->>MW: authenticate(JWT)
MW-->>API: user verified
API->>Parser: extract text + skills (pdf-parse/mammoth)
Parser->>DB: INSERT candidates, resumes
API-->>FE: 201 Created (parsed_data, quality_score)
U->>FE: Clicks "Match Candidates"
FE->>API: POST /api/candidates/match/:jobId
API->>Match: calculateMatch(candidate, job)
Match->>DB: SELECT job requirements
Match-->>API: match_score + score_breakdown
API->>DB: UPSERT applications
API-->>FE: Ranked candidate list
Replace these placeholders with real screenshots from
/docs/screenshots/.
| Login | Dashboard |
|---|---|
![]() |
![]() |
| Candidate Matching | Job Builder |
|---|---|
![]() |
![]() |
| Analytics | Resume Upload |
|---|---|
![]() |
![]() |
| Layer | Technologies |
|---|---|
| Frontend | React 18, Vite, React Router 6, Zustand, Axios, Recharts, React Dropzone, Tailwind CSS, Lucide Icons, React Hot Toast |
| Backend | Node.js 20, Express 4, express-async-errors, express-validator |
| Database | MySQL 8.0 (mysql2 driver), normalized relational schema with UUID primary keys |
| Authentication | JWT (access + refresh tokens), bcryptjs password hashing, email OTP verification |
| File Processing | Multer (uploads), pdf-parse, mammoth (DOCX parsing) |
| Nodemailer over SMTP (Gmail App Passwords) | |
| Security | Helmet, CORS, express-rate-limit, centralized error handler |
| Observability | Winston structured logging, Morgan HTTP request logs |
| Containerization | Docker (multi-stage builds), Docker Compose, Nginx (frontend static serving) |
| Orchestration | Kubernetes (Deployments, Services, Ingress, ConfigMap, Secret, PVC) |
| Deployment Targets | Vercel (frontend), Render/EKS (backend), Clever Cloud / managed MySQL |
Note on "AI": The matching, scoring, and job-description-generation engines in this codebase are deterministic, rules-based algorithms (weighted skill comparison, template generation) โ not third-party LLM API calls. This makes every score fully explainable and reproducible, which is a deliberate design choice for hiring use cases where decisions must be defensible.
AiHiringJob/
โโโ backend/
โ โโโ config/
โ โ โโโ database.js # MySQL connection pool + config validation
โ โโโ controllers/
โ โ โโโ authController.js # Register, login, OTP, refresh, profile
โ โ โโโ jobController.js # CRUD + description generation
โ โ โโโ candidateController.js # Matching, pipeline, interview Qs
โ โ โโโ resumeController.js # Upload, parse, list, delete
โ โ โโโ analyticsController.js # Dashboard + per-job analytics
โ โ โโโ notificationController.js
โ โโโ middleware/
โ โ โโโ authMiddleware.js # JWT verification
โ โ โโโ errorHandler.js # Centralized AppError handling
โ โ โโโ uploadMiddleware.js # Multer config
โ โโโ services/
โ โ โโโ matchingService.js # Weighted scoring engine
โ โ โโโ resumeParserService.js # Skill extraction + parsing
โ โ โโโ jobDescriptionService.js # Template-based JD generator
โ โ โโโ interviewService.js # Question bank generator
โ โ โโโ emailService.js # OTP + notification emails
โ โโโ routes/ # authRoutes, jobRoutes, resumeRoutes, ...
โ โโโ sql/
โ โ โโโ mysql-schema.sql # Full relational schema
โ โ โโโ notifications.sql
โ โโโ utils/logger.js
โ โโโ Dockerfile
โ โโโ .env.example
โ โโโ server.js / app.js
โโโ frontend/
โ โโโ src/
โ โ โโโ components/ # Breadcrumb, NotificationsDropdown, ProfileDropdown
โ โ โโโ layouts/DashboardLayout.jsx
โ โ โโโ pages/ # Login, Register, OTP, Dashboard, JobBuilder, Candidates...
โ โ โโโ services/api.js # Axios instance
โ โ โโโ store/authStore.js # Zustand auth store
โ โโโ Dockerfile
โ โโโ nginx.conf
โ โโโ vercel.json
โโโ kubernetes/
โ โโโ deployment.yaml # Backend + Frontend Deployments
โ โโโ k8s-resources.yaml # Services, ConfigMap, Secret, PVC, Ingress
โโโ docker-compose.yml # mysql + backend + frontend orchestration
โโโ README.md
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /register |
Create a recruiter account | Public |
| POST | /login |
Authenticate, issue JWT pair | Public |
| POST | /verify-otp |
Verify OTP sent to email | Public |
| POST | /resend-otp |
Resend OTP code | Public |
| POST | /refresh |
Exchange refresh token for new access token | Public |
| POST | /logout |
Invalidate session | ๐ |
| GET | /me |
Get current user profile | ๐ |
| PUT | /update-profile |
Update recruiter profile | ๐ |
| Method | Endpoint | Description |
|---|---|---|
| POST | / |
Create a job posting |
| GET | / |
List jobs |
| GET | /:id |
Get a single job |
| PUT | /:id |
Update a job |
| DELETE | /:id |
Delete a job |
| POST | /generate-description |
Auto-generate a job description |
| Method | Endpoint | Description |
|---|---|---|
| POST | /upload |
Bulk upload up to 20 resumes (multipart) |
| GET | / |
List parsed resumes |
| GET | /:id |
Get resume detail + parsed data |
| DELETE | /:id |
Delete a resume |
| Method | Endpoint | Description |
|---|---|---|
| GET | / |
List candidates |
| GET | /:id |
Candidate detail |
| POST | /match/:jobId |
Run matching engine against a job |
| PUT | /pipeline/:applicationId |
Update pipeline stage |
| POST | /:candidateId/interview-questions/:jobId |
Generate interview questions |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/analytics/dashboard |
Aggregate hiring metrics |
| GET | /api/analytics/jobs/:jobId |
Per-job funnel analytics |
| GET | /api/notifications |
List notifications |
| PATCH | /api/notifications/:id/read |
Mark notification as read |
All routes other than /auth/register, /auth/login, /auth/verify-otp, /auth/resend-otp, and /auth/refresh require a valid Authorization: Bearer <token> header.
erDiagram
USERS ||--o{ JOBS : creates
USERS ||--o{ CANDIDATES : owns
USERS ||--o{ RESUMES : owns
USERS ||--o{ NOTIFICATIONS : receives
USERS ||--|| RECRUITERS : "has profile"
JOBS ||--o{ APPLICATIONS : receives
CANDIDATES ||--o{ APPLICATIONS : submits
CANDIDATES ||--o{ RESUMES : has
RESUMES ||--o| APPLICATIONS : "linked to"
USERS {
char id PK
string name
string email
string password_hash
string role
boolean is_verified
string otp_code
datetime otp_expires_at
}
RECRUITERS {
char id PK
char user_id FK
string company_name
string job_title
}
JOBS {
char id PK
char user_id FK
string title
json required_skills
json nice_to_have_skills
int experience_min
int experience_max
string status
}
CANDIDATES {
char id PK
char user_id FK
string name
json skills
int experience_years
json education
}
RESUMES {
char id PK
char candidate_id FK
string filename
json parsed_data
int quality_score
boolean is_duplicate
}
APPLICATIONS {
char id PK
char job_id FK
char candidate_id FK
char resume_id FK
int match_score
json score_breakdown
string pipeline_stage
}
NOTIFICATIONS {
char id PK
char user_id FK
text message
boolean is_read
}
- Node.js โฅ 20
- MySQL โฅ 8.0 (or use the bundled Docker MySQL container)
- npm
git clone https://github.com/GIRICHANDAN125/AiHiringJob.git
cd AiHiringJobcd backend
cp .env.example .env # fill in DB + JWT + SMTP values
npm install
npm run dev # nodemon, http://localhost:5000 (or 5002)cd frontend
npm install
npm run dev # Vite dev servermysql -u root -p < backend/sql/mysql-schema.sql
mysql -u root -p < backend/sql/notifications.sqlNODE_ENV=production
PORT=5002
FRONTEND_URL=https://your-vercel-app.vercel.app
DB_HOST=
DB_PORT=3306
DB_NAME=
DB_USER=
DB_PASSWORD=
# Or: DATABASE_URL=mysql://user:password@host:port/dbname
JWT_SECRET=
JWT_REFRESH_SECRET=
JWT_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
SMTP_USER=your-email@gmail.com
SMTP_PASS=xxxx xxxx xxxx xxxx
EMAIL_FROM=your-email@gmail.com
UPLOAD_DIR=./uploads
MAX_FILE_SIZE=10485760The project ships with a full Docker Compose stack: MySQL โ Backend โ Frontend, wired together with health checks and dependency ordering.
cp backend/.env.example backend/.env
docker-compose up --build| Service | Image | Port | Notes |
|---|---|---|---|
mysql |
mysql:8.0 |
3306 | Healthcheck via mysqladmin ping |
backend |
built from backend/Dockerfile (node:20-alpine) |
5000 | Waits for MySQL healthcheck, runs as non-root node user |
frontend |
multi-stage build โ nginx:1.27-alpine |
3000 โ 80 | Serves Vite production build via Nginx |
Build an individual image:
docker build -t ai-hiring-backend ./backend
docker build -t ai-hiring-frontend ./frontend --build-arg VITE_API_URL=/apiProduction-style manifests live in kubernetes/:
- Deployments โ
backendandfrontend, 2 replicas each, with CPU/memory requests & limits, liveness and readiness probes (/healthfor backend,/for frontend). - Services โ
backend-serviceandfrontend-service(ClusterIP). - ConfigMap โ non-secret runtime config (
NODE_ENV,DB_HOST, etc.). - Secret โ
JWT_SECRET, DB credentials, SMTP credentials (replace placeholder values before applying). - PersistentVolumeClaim โ
ReadWriteMany, 10Gi, for the shared/app/uploadsvolume across backend pods. - Ingress โ Nginx ingress class with TLS via
cert-manager(letsencrypt-prodcluster issuer), routing/apiโ backend,/โ frontend.
kubectl create namespace ai-hiring
kubectl apply -f kubernetes/ -n ai-hiring
kubectl get pods -n ai-hiring -w- Backend pods are stateless โ scale horizontally with
kubectl scale deployment/backend --replicas=Nor attach a Horizontal Pod Autoscaler (HPA) on CPU utilization (not yet included โ see Future Enhancements). - The shared uploads PVC means any replica can serve any user's files, avoiding sticky-session requirements Improved project overview.
Current state: No GitHub Actions / CI pipeline is present in the repository yet. The suggested pipeline below reflects the natural next step given the existing Docker and Kubernetes assets, and is listed under Future Enhancements.
flowchart LR
A[Push to main] --> B[Lint & Test]
B --> C[Build Docker Images]
C --> D[Push to Registry]
D --> E[kubectl apply -f kubernetes/]
E --> F[Rolling Update on Cluster]
| Component | Target |
|---|---|
| Frontend | Vercel (static build, VITE_API_URL env var) |
| Backend | Render / Railway / AWS EKS (containerized) |
| Database | Clever Cloud MySQL / Amazon RDS for MySQL |
| File Storage | Persistent volume (local) or swap for S3-compatible storage at scale |
The live demo (https://ai-hiring-job.vercel.app) follows this exact split: Vercel for the SPA, a managed Node host for the API, and a managed MySQL instance for persistence.
- JWT Authentication โ short-lived access tokens (15 min) paired with longer-lived refresh tokens (7 days) to limit exposure of compromised tokens.
- Password Hashing โ
bcryptjswith salted hashing; raw passwords are never stored or logged. - Email OTP Verification โ accounts must verify a one-time code before becoming active.
- API Protection โ
helmetsecurity headers, strict CORS policy, and tiered rate limiting (general API vs. auth endpoints). - Environment Security โ all secrets (
JWT_SECRET, DB credentials, SMTP credentials) are externalized via.env/ Kubernetes Secrets and never committed to source control. - Centralized Error Handling โ
AppError+errorHandlermiddleware prevent stack traces or internal details from leaking to clients.
- Compression โ
compressionmiddleware gzips API responses. - Connection Pooling โ
mysql2connection pool avoids per-request connection overhead. - Indexed Queries โ targeted indexes on
applications(job_id),applications(candidate_id),resumes(user_id), andnotifications(user_id, created_at)keep dashboard and matching queries fast as data scales. - Multi-Stage Docker Builds โ frontend image discards the Node build toolchain, shipping only static assets via Nginx for a minimal runtime footprint.
- Duplicate Detection โ hash-based dedup avoids re-parsing and re-scoring identical resumes.
- Real LLM-backed semantic resume matching (embeddings) as an optional upgrade path over the current rules engine
- GitHub Actions CI/CD pipeline (lint โ test โ build โ deploy)
- Horizontal Pod Autoscaler (HPA) for the backend Deployment
- Redis caching layer for analytics/dashboard queries
- S3-compatible object storage for resumes instead of local PVC
- Role-based access control for multi-recruiter organizations
- Candidate-facing portal for self-service applications
- Calendar integration for interview scheduling
- Webhooks for ATS (Applicant Tracking System) integrations
- Resume parsing support for additional formats (RTF, scanned PDFs via OCR)
- Audit logging for compliance-sensitive hiring decisions
- Multi-language resume parsing
- Automated email digests of weekly hiring funnel metrics
- Dark mode for the dashboard
- End-to-end test suite (Playwright/Cypress) and backend integration tests
Chandu Giri Full Stack Developer ยท B.Tech Computer Science & Engineering
If you run into issues or have questions:
- Open an issue
- Start a discussion
- Reach out directly via the contact links above
If this project helped you, consider giving it a โญ โ it genuinely helps visibility.
This project is licensed under the MIT License. See LICENSE for details.
Made with โ and a lot of console.log by Chandu Giri





