An AI-powered resume analyser and HR outreach tool — built as a full-stack portfolio project demonstrating AI integration, file handling, and automated email delivery.
Live: https://resumeforge-1-c1t9.onrender.com
- 📤 PDF Resume Upload — Multer
memoryStorage— file stored in buffer, never written to disk - 🤖 AI-Powered Analysis — section-wise resume scoring against a target job role using Groq's LLaMA 3.3 70B
- 📊 Structured Feedback — overall score (0–100), section scores (0–10), top strengths, critical gaps, missing ATS keywords, and side-by-side suggested rewrites
- 📧 AI-Generated HR Email — personalised cover email generated by AI and delivered via Brevo REST API with resume attached as PDF
- 🎨 Dark / Light Theme — CSS custom property system with
data-themetoggle, persisted in localStorage - 🔁 Stateless Architecture — no database, no auth, no stored files. Resume processed in-memory and discarded after response
| Layer | Technology |
|---|---|
| Backend | Node.js + Express.js (ES Modules) |
| Frontend | React.js (Vite) + Tailwind CSS + DaisyUI |
| AI | Groq SDK — llama-3.3-70b-versatile |
| Brevo REST API (HTTP-based, no SMTP) | |
| PDF Parse | pdf2json with safeDecode wrapper |
| File Upload | Multer with memoryStorage() |
| HTTP Client | Axios with centralized API layer |
| Routing | React Router v6 |
| Deployment | Render (free tier) |
resumeforge/
├── backend/
│ ├── src/
│ │ ├── controller/
│ │ │ ├── resume.controller.js ← POST /api/resume/analyse
│ │ │ └── email.controller.js ← POST /api/email/send
│ │ ├── middleware/
│ │ │ └── upload.middleware.js ← Multer (memoryStorage, PDF-only, 5MB)
│ │ ├── routes/
│ │ │ ├── resume.route.js
│ │ │ └── email.route.js
│ │ ├── services/
│ │ │ ├── ai.service.js ← Groq API + prompt engineering + JSON parsing
│ │ │ ├── pdf.service.js ← pdf2json text extraction
│ │ │ └── email.service.js ← Brevo API email delivery
│ │ └── utils/
│ │ └── env.js ← destructured process.env exports
│ └── app.js ← Express setup, CORS, route registration
│
└── frontend/
└── src/
├── components/
│ ├── Navbar.jsx
│ └── ResumeUpload.jsx
├── context/
│ └── ThemeContext.jsx ← dark/light theme via Context API
├── lib/
│ └── api.js ← centralized Axios instance
├── pages/
│ ├── Home.jsx
│ ├── Analysis.jsx
│ ├── EmailPreview.jsx
│ └── Success.jsx
└── App.jsx
User fills form → Name, Target Job Role, HR Email, PDF Upload
↓
POST /api/resume/analyse (multipart/form-data)
↓
Multer → PDF stored in req.file.buffer (never hits disk)
↓
pdf2json → extracts raw text from buffer
↓
Groq API (LLaMA 3.3 70B) → receives resumeText + jobRole
↓
AI returns strict JSON → { email: {subject, body}, analysis: {...} }
↓
Backend validates → strips markdown fences → parses JSON → checks required fields
↓
Frontend → /analysis page (data passed via React Router state)
↓
User reviews → clicks "Preview & Send Email"
↓
POST /api/email/send → re-runs extraction + AI → Brevo sends email with PDF attachment
↓
/email-preview → user confirms → /success
git clone https://github.com/coder-Rishi05/ResumeForge.git
cd ResumeForgecd backend
npm installCreate .env:
PORT=3000
API_KEY=your_groq_api_key
BREVO_API_KEY=your_brevo_api_key
EMAIL_USER=your_verified_sender@email.comnpm run devcd frontend
npm install
npm run devFrontend: http://localhost:5173 | Backend: http://localhost:3000
Analyse a resume against a target job role.
Request — multipart/form-data
| Field | Type | Description |
|---|---|---|
resume |
File | PDF resume (max 5MB) |
jobRole |
String | Target job role |
Response
{
"success": true,
"email": { "subject": "...", "body": "..." },
"analysis": {
"overall_score": 72,
"overall_summary": "...",
"top_strengths": ["..."],
"critical_gaps": ["..."],
"keywords_to_add": ["..."],
"sections": {
"technical_skills": {
"score": 8,
"summary": "...",
"strengths": ["..."],
"suggestions": [{ "text": "...", "priority": "High" }],
"suggested_rewrites": [{ "current": "...", "improved": "..." }]
},
"projects": { "...": "..." },
"experience": { "...": "..." },
"education": { "...": "..." }
}
},
"meta": { "jobRole": "...", "fileSize": "120.5 KB", "fileName": "resume.pdf" }
}Generate AI cover email and send via Brevo with resume attached.
Request — multipart/form-data
| Field | Type | Description |
|---|---|---|
resume |
File | PDF resume |
to |
String | HR's email address |
jobRole |
String | Target job role |
applicantName |
String | Applicant's full name |
Response
{
"success": true,
"message": "Email successfully sent to hr@company.com",
"analysis": { "...": "..." },
"meta": {
"recipient": "hr@company.com",
"subject": "Application for Backend Developer — Rishabh Singh",
"jobRole": "Backend Developer",
"attachedFile": "resume.pdf",
"fileSize": "120.5 KB"
}
}| Problem | Decision |
|---|---|
| Render free tier blocks SMTP ports (25, 465, 587) | Switched from Nodemailer/Gmail SMTP → Brevo HTTP REST API |
pdf-parse ES Module import errors |
Switched to pdf2json with custom safeDecode for URL-encoded characters |
| LLaMA sometimes returns markdown-wrapped JSON | Post-process: strip ```json fences → regex extract {...} → validate required fields |
| Ephemeral filesystem on Render | Multer memoryStorage — PDF lives only in req.file.buffer, no disk writes, no cleanup needed |
| Linear 4-step flow needs no global state | React Router navigate(path, { state }) — clean, no Redux overhead |
| Variable | Required | Description |
|---|---|---|
PORT |
No | Server port (default: 3000) |
API_KEY |
Yes | Groq API key |
BREVO_API_KEY |
Yes | Brevo transactional email API key |
EMAIL_USER |
Yes | Verified sender email in Brevo |
- Push backend to GitHub
- Create Web Service on Render
- Build Command:
npm install| Start Command:node app.js - Add environment variables in Render dashboard
- Deploy — auto-deploys on push to
main
Note: Render free tier blocks SMTP. Brevo's HTTP REST API bypasses this completely.
- Resume score history (localStorage)
- Support for
.docxuploads - Improved section-level prompt tuning
- UI polish pass with proper Tailwind utilities