ResumeXpert is a full-stack AI resume analyzer. Upload your PDF resume, paste a job description, and get instant, structured feedback powered by Google Gemini — covering ATS compatibility, tone & style, content quality, structure, and skills alignment.
┌─────────────────────────────────────────────────────────────┐
│ │
│ Upload PDF → Gemini AI Analysis → Structured Score │
│ │
│ ATS Score · Tone & Style · Content · Structure · Skills │
│ │
└─────────────────────────────────────────────────────────────┘
|
|
|
|
| Layer | Technology |
|---|---|
| Framework | React Router v7 (SSR) |
| Language | TypeScript |
| Styling | Tailwind CSS v4 |
| Auth | Custom JWT + bcrypt |
| Database | Neon Postgres |
| ORM | Prisma v7 + Neon Adapter |
| AI | Google Gemini 2.0 Flash |
| File Storage | Vercel Blob |
| Validation | Zod |
| Deployment | Vercel |
📦 ai-resume-analyzer
├── 📂 app
│ ├── 📂 components # UI components
│ │ ├── Navbar.tsx # Navigation with auth state
│ │ ├── ResumeCard.tsx # Resume preview card
│ │ ├── ScoreCircle.tsx # Circular score indicator
│ │ ├── ScoreGauge.tsx # Gauge chart for overall score
│ │ ├── Summary.tsx # Score summary panel
│ │ ├── ATS.tsx # ATS analysis section
│ │ ├── Details.tsx # Detailed accordion feedback
│ │ └── FileUploader.tsx # Drag & drop PDF uploader
│ ├── 📂 lib
│ │ ├── auth.server.ts # JWT, bcrypt, cookie helpers
│ │ ├── db.server.ts # Prisma + Neon client singleton
│ │ ├── env.server.ts # Typed env accessors (lazy)
│ │ ├── session.server.ts # requireUser / optionalUser
│ │ └── storage.server.ts # Vercel Blob upload/delete
│ ├── 📂 routes
│ │ ├── home.tsx # Dashboard — all resumes
│ │ ├── auth.tsx # Sign in / Sign up
│ │ ├── logout.tsx # Clear cookie + redirect
│ │ ├── upload.tsx # Upload form + Gemini analysis
│ │ ├── resume.tsx # Resume detail + feedback
│ │ ├── resume-pdf.tsx # PDF proxy (stream from Blob)
│ │ └── wipe.tsx # Delete all user resumes
│ ├── root.tsx # App shell
│ └── app.css # Global styles
├── 📂 prisma
│ └── schema.prisma # DB schema (User + Resume)
├── 📂 resumeData
│ └── index.ts # Demo resumes + Gemini prompt
├── .env.example # Environment variable template
└── vercel.json # Vercel deployment config
- Node.js 20+
- Neon Postgres database (free tier works)
- Google AI Studio Gemini API key
- Vercel account + Blob store
git clone https://github.com/your-username/ai-resume-analyzer.git
cd ai-resume-analyzer
npm installcp .env.example .envOpen .env and fill in:
# Neon Postgres — from neon.tech dashboard
DATABASE_URL="postgresql://user:pass@host/db?sslmode=require"
# JWT secret — any random 32+ character string
# Generate one: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
JWT_SECRET="your-super-secret-key-minimum-32-characters"
# Gemini API key — from aistudio.google.com/apikey (starts with AIza...)
GEMINI_API_KEY="AIzaSy..."
# Vercel Blob — from Vercel Dashboard → Storage → Blob → Token
BLOB_READ_WRITE_TOKEN="vercel_blob_rw_..."npm run db:pushnpm run devOpen http://localhost:5173 🎉
git add .
git commit -m "feat: initial commit"
git push origin main- Go to vercel.com/new
- Import your GitHub repository
- Framework auto-detected as React Router
Vercel Dashboard → Storage → your Blob store → Projects → connect this project
This auto-injects BLOB_READ_WRITE_TOKEN into your deployment.
In Vercel → Project → Settings → Environment Variables:
| Key | Value |
|---|---|
DATABASE_URL |
Your Neon connection string |
JWT_SECRET |
Your secret key |
GEMINI_API_KEY |
Your Gemini API key |
BLOB_READ_WRITE_TOKENis auto-added by the Blob store connection above.
Every push to main triggers a new deployment automatically.
User uploads PDF + job description
│
▼
Server validates input
│
▼
PDF → Vercel Blob (stored, returns public URL)
│
▼
Resume record saved to Neon DB (feedback: null)
│
▼
PDF sent to Gemini as base64 inline data
│
▼
Gemini returns structured JSON feedback
│
▼
DB record updated with feedback JSON
│
▼
User redirected to /resume/:id
│
┌────┴────┐
▼ ▼
PDF shown AI feedback
(via proxy) (scores + tips)
Idempotency: Uploading the same company + job title twice returns the cached result instantly — no duplicate Gemini calls, no duplicate DB records.
Each resume is scored across 5 categories, each out of 100:
| Category | What it measures |
|---|---|
| 🎯 ATS Score | Keyword matching, formatting compatibility with applicant tracking systems |
| 🗣️ Tone & Style | Professional language, consistency, readability |
| 📝 Content | Achievements, impact statements, relevance to job description |
| 🏗️ Structure | Layout, sections, visual hierarchy |
| 🛠️ Skills | Technical and soft skill alignment with the role |
npm run dev # Start development server
npm run build # Production build
npm run start # Serve production build
npm run typecheck # TypeScript type checking
npm run db:push # Push schema to database
npm run db:studio # Open Prisma Studio (visual DB browser)
npm run db:generate # Regenerate Prisma client| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
✅ | Neon Postgres connection string |
JWT_SECRET |
✅ | Secret key for JWT tokens (min 32 chars) |
GEMINI_API_KEY |
✅ | Google Gemini API key (starts with AIza) |
BLOB_READ_WRITE_TOKEN |
✅ | Vercel Blob storage token |
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-feature - Commit your changes:
git commit -m 'feat: add my feature' - Push to the branch:
git push origin feat/my-feature - Open a pull request
MIT © Abhay Gautam