A precision tool for ambitious job seekers. Use neural-matching to bypass the noise and land high-stakes opportunities.
- Project Overview
- Key Features
- Tech Stack
- Architecture
- Getting Started
- Project Structure
- Development Workflow
- Contributing
- Troubleshooting
- License
CVPilot is an intelligent resume-to-job matching platform that helps job seekers discover and apply to the most relevant opportunities. Using advanced AI and natural language processing, CVPilot:
- Analyzes your resume to extract skills, experience, and career profile
- Matches you with live job listings from the Active Jobs database
- Scores jobs intelligently (0-100%) based on skill alignment and requirements
- Generates ATS optimization tips tailored to specific job descriptions
- Tracks preferences (seniority level, location type) for personalized recommendations
User Signs Up (Clerk Auth)
↓
Upload Resume (PDF/DOCX)
↓
AI Parses Resume (Google Gemini)
↓
Set Job Preferences (Location, Seniority)
↓
Browse Jobs (Active Jobs API)
↓
AI Matches Resume vs Job (LLM Scoring)
↓
View Ranked Results (Score + Confidence)
↓
Get ATS Optimization Tips
↓
Apply with Confidence
| Feature | Description | Status |
|---|---|---|
| Smart Resume Upload | Upload PDF/DOCX files; AI extracts structure automatically | ✅ Production |
| AI Resume Parsing | Google Gemini extracts skills, experience, seniority level | ✅ Production |
| Job Matching | LLM-based scoring matches your profile vs job requirements | ✅ Production (V2) |
| Match Confidence | See not just scores but confidence levels (0-1) | ✅ Production |
| Preference Management | Set seniority & location preferences for personalized jobs | ✅ Production |
| ATS Optimization | Get resume tweaks to pass ATS scanners for specific jobs | ✅ Production |
| Job Tracking | View your matched jobs and scores in one dashboard | ✅ Production |
| Feature | Description |
|---|---|
| Type-Safe APIs | Full TypeScript throughout frontend & backend |
| Clean Architecture | Layered design (controller → service → DB) |
| Comprehensive Docs | API docs, setup guides, architecture explanations |
| Testing Framework | Unit tests for services, integration tests for routes |
| Error Handling | Centralized error middleware with correlation IDs |
| Structured Logging | JSON-formatted logs with context (userId, duration, etc.) |
| CI/CD Ready | Pipeline-friendly build steps and database migrations |
React 19 - Component library with hooks, server components
TypeScript 5.9 - Static type checking for frontend code
Vite 8 - Ultra-fast build tool with HMR
Tailwind CSS 3.4 - Utility-first styling framework
React Router DOM 7 - Client-side routing and navigation
Clerk @clerk/react - Authentication (sign-up, login, sessions)
pdfjs-dist 5.6 - In-browser PDF rendering
Mammoth 1.12 - Word document (.docx) conversion
Node.js 18+ - JavaScript runtime
Express 5.2 - Web framework and HTTP routing
TypeScript 6 - Type-safe backend code
PostgreSQL 13+ - Relational database
Prisma 7.7 - Type-safe ORM with migrations
@clerk/express 2.1 - Backend auth middleware for Clerk
@google/generative-ai 0.21 - Google Gemini API for AI features
Cloudinary 2.9 - CDN file storage for resumes
Zod 4.3 - Runtime input validation
TSX 4.21 - TypeScript execution for dev
pnpm 8+ - Fast, disk-efficient package manager
Prisma CLI - Database schema & migration tools
tsx watch - Hot-reload for backend development
ESLint + Prettier - Code quality & formatting
Docker - Container deployment (optional)
PM2 - Production process manager (optional)
┌─────────────────────────────────────────────────────────────────┐
│ FRONTEND (React) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Landing Page │ │ Profile Page │ │ Job Page │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ↓ ↓ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Clerk Authentication (OAuth/Email/SSO) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↕ (HTTP/JSON)
┌─────────────────────────────────────────────────────────────────┐
│ BACKEND (Express.js) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ API Routes: /resume, /jobs, /auth, /preferences │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Controllers: HTTP adapters (validation, responses) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Services: Business logic (matching, parsing, storage) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Middleware: Auth (Clerk), validation (Zod), errors │ │
│ └────────────────────────────────────────────────────────��─┘ │
└─────────────────────────────────────────────────────────────────┘
↓ ↓ ↓
┌─────────────┐ ┌──────────────────┐ ┌───────────────────┐
│ PostgreSQL │ │ Cloudinary CDN │ │ Google Gemini │
│ (Prisma) │ │ (File Storage) │ │ (AI Scoring) │
└─────────────┘ └──────────────────┘ └───────────────────┘
↓
┌─────────────────────────────────────┐
│ Active Jobs API (Job Listings) │
└─────────────────────────────────────┘
REQUEST FLOW:
─────────────────────────────────────────────────────────────
HTTP Request
↓
MIDDLEWARE LAYER
├─ Body parsing (JSON)
├─ CORS validation
├─ Clerk JWT verification
├─ Input validation (Zod)
└─ Error handling
↓
ROUTE LAYER
└─ Route matching & HTTP method handling
↓
CONTROLLER LAYER
├─ Extract request parameters
├─ Call service functions
└─ Format HTTP response
↓
SERVICE LAYER (Business Logic)
├─ Orchestration (multi-step workflows)
├─ Domain rules & validation
├─ LLM calls (Gemini API)
├─ Job matching algorithms
└─ Transaction management
↓
LIB LAYER (Singletons)
├─ Prisma client instance
├─ Logger factory
├─ Cloud storage client
└─ Configuration loaders
↓
DATABASE & EXTERNAL SERVICES
├─ PostgreSQL (Prisma ORM)
├─ Cloudinary (file storage)
├─ Google Gemini (AI)
└─ Active Jobs API (job listings)
↓
SERVICE LAYER (responses flow back)
↓
CONTROLLER LAYER (transforms)
↓
HTTP RESPONSE (JSON)
backend/
├── server.ts # Express app entry point
├── package.json # Dependencies
├── tsconfig.json # TypeScript config
│
├── config/ # Configuration
│ └── environment validation
│
├── route/ # Route definitions
│ ├── auth.route.ts
│ ├── resume.route.ts
│ ├── jobs.route.ts
│ └── preferences.route.ts
│
├── controller/ # HTTP handlers
│ ├── authController.ts
│ ├── resumeController.ts
│ ├── jobsController.ts
│ └── preferencesController.ts
│
├── middleware/ # Express middleware
│ ├── auth.middleware.ts # Clerk validation
│ ├── validation.middleware.ts # Zod schemas
│ ├── errorHandler.middleware.ts
│ └── upload.middleware.ts # Multer for files
│
├── service/ # Business logic
│ ├── resume/
│ │ ├── resumeService.ts
│ │ ├── fileDownloader.ts
│ │ └── parser.ts
│ │
│ ├── jobs/
│ │ ├── jobsService.ts
│ │ ├── llmJobMatcherV2.ts # AI scoring
│ │ ├── providers/
│ │ │ └── activeJobs.provider.ts
│ │ ├── matchCache.ts # In-memory cache
│ │ └── jobMatch.service.ts
│ │
│ ├── preferences/
│ │ └── preferencesService.ts
│ │
│ ├── user/
│ │ └── userService.ts
│ │
│ └── geminiClient.ts # Google AI setup
│
├── lib/ # Shared utilities
│ ├── prisma.ts # Singleton Prisma
│ ├── logger.ts # Logging utility
│ ├── cloudinary.ts # File storage
│ └── validators.ts # Zod schemas
│
├── prisma/ # Database
│ ├── schema.prisma # DB schema
│ ├── migrations/ # Version-controlled migrations
│ └── seed.ts # Seed data
│
├── scripts/ # Developer helpers
│ └── fix-prisma-imports.mjs
│
├── tests/ # Test suite
│ ├── jobs.test.ts
│ ├── greenhouse.provider.test.ts
│ └── active-jobs.real-api.test.ts
│
├── docs/ # Documentation
│ ├── IMPLEMENTATION_CHECKLIST.md
│ ├── V2_MIGRATION_GUIDE.md
│ ├── JOB_MATCHING_ROOT_CAUSE_ANALYSIS.md
│ └── EXECUTIVE_SUMMARY.md
│
└── README.md # Backend setup & architecture
DOCUMENTATION.md # Complete API reference
- Controllers handle HTTP only (request validation, response formatting)
- Services contain business logic (no HTTP knowledge)
- Middleware handles cross-cutting concerns (auth, logging, errors)
- Lib provides singletons (Prisma, logger, config)
// ❌ BAD: Errors leak stack traces to client
res.status(500).json({ error: err.stack });
// ✅ GOOD: User-friendly messages with logging
logger.error("OPERATION_FAILED", {
userId,
error: err.message,
stack: err.stack,
requestId: req.id,
});
res.status(500).json({
error: "Operation failed",
requestId: req.id, // For support debugging
});import { z } from "zod";
const createPreferencesSchema = z.object({
seniority: z.enum(["INTERN", "FULLTIME"]),
locationPreferences: z
.array(z.enum(["ONSITE", "HYBRID", "REMOTE"]))
.min(1),
});
// TypeScript knows the shape after validation
const validated = createPreferencesSchema.parse(req.body);
// ✅ validated is strictly typed// In-memory LRU cache for job matches
const cache = new MatchCache(maxEntries: 10000);
export async function matchJobs(userId, resume) {
const cacheKey = `${userId}:${resumeHash}`;
// Check cache first
const cached = cache.get(userId, jobId);
if (cached) return cached; // Cache hit!
// Cache miss: run expensive LLM pipeline
const result = await runLLMMatching(resume, jobs);
// Store in cache
cache.set(userId, jobId, result);
return result;
}Before you begin, ensure you have:
- Node.js v18 or higher (Download)
- pnpm v8 or higher (
npm install -g pnpm) - PostgreSQL 13+ (Download)
- Git for version control
git clone https://github.com/yb175/CVPilot.git
cd CVPilot# Install both backend & frontend dependencies (monorepo)
pnpm installcd backend
# Copy environment template
cp .env.sample .env
# ⚠️ IMPORTANT: Edit .env with your credentials
# Required variables:
# - DATABASE_URL=postgresql://user:password@localhost:5432/cvpilot
# - CLERK_SECRET_KEY=sk_test_...
# - CLERK_PUBLISHABLE_KEY=pk_test_...
# - GOOGLE_GEMINI_API_KEY=AIzaSy...
# - CLOUDINARY_CLOUD_NAME=...
# - CLOUDINARY_API_KEY=...
# - CLOUDINARY_API_SECRET=...
# - RAPIDAPI_KEY=...
# Generate Prisma client
pnpm prisma generate
# Apply database migrations
pnpm prisma migrate dev --name init
# Verify database setup
pnpm prisma studio
cd ..cd frontend
# Copy environment template
cp .env.sample .env
# ⚠️ IMPORTANT: Edit .env with your credentials
# Required variables:
# - VITE_CLERK_PUBLISHABLE_KEY=pk_test_...
# - VITE_API_URL=http://localhost:3000
cd ..Open two terminal windows:
Terminal 1: Backend Server
cd backend
pnpm run dev
# Output:
# [Server] Gemini client initialized
# Server started on port 3000Terminal 2: Frontend Dev Server
cd frontend
pnpm run dev
# Output:
# VITE v8.0.1 ready in 256 ms
# ➜ Local: http://localhost:5173/
# ➜ press h to show help- Frontend: http://localhost:5173
- Backend API: http://localhost:3000
- API Docs: http://localhost:3000/api/docs (if Swagger is enabled)
- Prisma Studio:
cd backend && pnpm prisma studio
# Test API health check
curl http://localhost:3000/health
# Expected output: OKCVPilot/
├── backend/ # Node.js API server
│ ├── README.md # Backend-specific docs
│ ├── DOCUMENTATION.md # Complete API reference
│ ├── server.ts # Express app entry
│ ├── package.json
│ ├── tsconfig.json
│ ├── src/
│ │ ├── controller/
│ │ ├── service/
│ │ ├── route/
│ │ ├── middleware/
│ │ ├── lib/
│ │ └── tests/
│ ├── prisma/
│ │ ├── schema.prisma
│ │ └── migrations/
│ └── docs/
│
├── frontend/ # React SPA
│ ├── README.md # Frontend-specific docs
│ ├── src/
│ │ ├── main.tsx # App entry point
│ │ ├── App.tsx # Root component
│ │ ├── pages/ # Route pages
│ │ ├── components/ # Reusable components
│ │ ├── services/ # API utilities
│ │ ├── lib/ # Helpers
│ │ ├── styles/ # Global CSS
│ │ └── types/ # TypeScript types
│ ├── index.html
│ ├── package.json
│ ├── tsconfig.json
│ └── vite.config.ts
│
├── package.json # Root monorepo config
├── pnpm-workspace.yaml # pnpm monorepo setup
├── README.md # This file
├── LICENSE # Proprietary license
└── .gitignore
- controllers/ — HTTP request handlers (thin adapters)
- services/ — Business logic & orchestration
- routes/ — API endpoint definitions
- middleware/ — Auth, validation, error handling
- lib/ — Shared utilities (Prisma, logger, config)
- tests/ — Unit and integration tests
- pages/ — Full-page components (routed)
- components/ — Reusable UI components
- services/ — API calls & data fetching
- lib/ — Helper functions, utilities
- types/ — TypeScript interfaces
cd backend
# Make code changes in src/
# TypeScript will compile automatically (tsx watch)
# Errors show in terminal immediately
# Tests (manual check)
pnpm run test
# Build for production
pnpm build
# Output: dist/server.jscd frontend
# Make code changes in src/
# Vite auto-reloads (HMR)
# Changes appear instantly in browser
# Type errors show in terminal
# Build for production
pnpm build
# Output: dist/ (static files)cd backend
# 1. Edit schema.prisma
# 2. Create migration
pnpm prisma migrate dev --name <migration_name>
# Example:
# pnpm prisma migrate dev --name add_user_verified_field
# This creates:
# - prisma/migrations/<timestamp>_<name>/migration.sql
# - Updates generated Prisma client
# 3. Commit to Git
git add prisma/migrations/
git commit -m "feat: add user verified field"cd backend
# Run all tests
pnpm run test:all
# Run specific test suite
pnpm run test # Job service tests
pnpm run test:provider # ATS provider tests
pnpm run test:active-jobs # Real API tests# Type checking (both packages)
pnpm run type-check
# Linting (when configured)
pnpm run lint
# Formatting (when configured)
pnpm run formatWe welcome contributions from the community! Here's how to get involved:
- Bug Fixes — Fix existing issues and improve stability
- Features — Add new functionality aligned with roadmap
- Documentation — Improve READMEs, docs, and comments
- Performance — Optimize latency, database queries, caching
- Tests — Increase code coverage and reliability
- ✅ Check open issues to avoid duplicates
- ✅ Review the architecture section to understand design patterns
- ✅ Read CONTRIBUTION_GUIDELINES.md for detailed guidelines
- ✅ Set up local development environment (follow Getting Started)
# Fork the repo on GitHub
# Then clone your fork
git clone https://github.com/YOUR_USERNAME/CVPilot.git
cd CVPilot
# Add upstream remote for syncing
git remote add upstream https://github.com/yb175/CVPilot.git# Sync with latest upstream
git fetch upstream
git checkout upstream/main
# Create feature branch
git checkout -b feature/my-feature-name
# Example branch names:
# - feature/resume-caching
# - fix/job-matching-score-bug
# - docs/api-reference
# - perf/optimize-db-queriesFollow the architecture patterns:
// Example: Adding a new service feature
// 1. Define Zod schema for inputs
// backend/lib/validators.ts
export const matchJobsSchema = z.object({
resumeHash: z.string(),
filters: z.object({
minScore: z.number().min(0).max(100),
}).optional(),
});
// 2. Create service function
// backend/service/jobs/matchingService.ts
export async function matchJobsImproved(
userId: number,
resume: ParsedResume,
filters?: MatchFilters
) {
// Validate inputs
const validated = matchJobsSchema.parse({ resumeHash, filters });
// Core logic
const scores = await runLLMMatching(resume);
// Filter
const filtered = scores.filter(s => s.score >= (filters?.minScore ?? 0));
// Persist
await persistMatchResults(userId, filtered);
return filtered;
}
// 3. Create controller handler
// backend/controller/jobsController.ts
export async function matchJobsHandler(req: Request, res: Response) {
const clerkUserId = req.auth?.userId;
const filters = req.body.filters;
const resume = await getResume(clerkUserId);
const results = await matchJobsImproved(userId, resume, filters);
res.json({
success: true,
count: results.length,
results,
});
}
// 4. Wire up route
// backend/route/jobsRoute.ts
router.post("/match", matchJobsHandler);
// 5. Add tests
// backend/tests/matching.test.ts
async function testMatchingImproved() {
const results = await matchJobsImproved(userId, resume, { minScore: 80 });
console.assert(results.every(r => r.score >= 80), "All scores >= 80");
}// backend/tests/my-feature.test.ts
import prisma from "../../lib/prisma.js";
import { myNewFunction } from "../../service/myNewService.js";
async function testMyFeature() {
console.log("🧪 Testing My Feature...\n");
try {
// Setup
const testUser = await prisma.user.create({
data: {
clerkId: `test_${Date.now()}`,
email: `test_${Date.now()}@example.com`,
},
});
// Test
const result = await myNewFunction(testUser.id);
console.assert(result !== null, "Result should not be null");
console.assert(result.success === true, "Result should indicate success");
// Cleanup
await prisma.user.delete({ where: { id: testUser.id } });
console.log("✅ Test passed!");
} catch (error) {
console.error("❌ Test failed:", error);
} finally {
await prisma.$disconnect();
}
}
testMyFeature();Run tests before committing:
pnpm run test:all# Use conventional commit format
git add .
git commit -m "feat(jobs): add minimum score filter to matching
- Add matchJobsImproved service function
- Support optional minScore filter
- Add validation with Zod schema
- Include tests for new functionality
- Resolves #123"
# Examples:
# feat(resume): add resume caching to reduce latency
# fix(matching): filter out jobs with missing IDs
# docs(api): update job matching endpoint documentation
# perf(db): add index to job_matches table
# test(service): add unit tests for resume parser# Push your feature branch
git push origin feature/my-feature-name
# Create PR on GitHub
# Title: Clear, descriptive
# Description: Explain what changed and why
# Link related issues: "Closes #123"PR Description Template:
## Description
Brief explanation of what this PR does.
## Changes
- Change 1
- Change 2
- Change 3
## Testing
How to test these changes:
1. Step 1
2. Step 2
## Related Issues
Closes #123
Fixes #456
## Checklist
- [ ] Code follows project architecture patterns
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] No breaking changes
- [ ] TypeScript compiles without errors- Respond to feedback promptly
- Make requested changes in follow-up commits
- Once approved, maintainers will merge
// ✅ DO: Use strict typing
async function getUserWithResume(userId: number): Promise<UserWithResume> {
return await prisma.user.findUnique({
where: { id: userId },
include: { resume: true },
});
}
// ❌ DON'T: Avoid 'any'
async function getUser(userId: any) {
return await prisma.user.findUnique({
where: { id: userId },
});
}
// ✅ DO: Use enums for fixed values
enum Seniority {
INTERN = "INTERN",
FULLTIME = "FULLTIME",
}
// ❌ DON'T: Use string literals everywhere
function setSeniority(seniority: "INTERN" | "FULLTIME") {
// ...
}// ✅ DO: Handle and log errors appropriately
try {
const result = await runExpensiveOperation();
return result;
} catch (error) {
logger.error("OPERATION_FAILED", {
userId,
error: error instanceof Error ? error.message : String(error),
});
throw new HttpError(500, "Operation failed");
}
// ❌ DON'T: Let errors propagate silently
try {
await runExpensiveOperation();
} catch (error) {
// Silently swallow error
}// ✅ DO: Use clear, descriptive names
const jobMatchScore = 87;
const getUserJobMatches = (userId: number) => {};
const MAXIMUM_CACHE_ENTRIES = 10000;
// ❌ DON'T: Use single-letter or cryptic names
const jms = 87;
const gu = (u: number) => {};
const MAX_CACHE = 10000;- Place business logic in service layer
- Keep controllers thin (validation + response formatting)
- Use Prisma for all database access (no raw SQL)
- Add tests for service functions
- Update DOCUMENTATION.md if API changes
- Use React hooks (no class components)
- Keep components small and focused
- Use TypeScript for all prop types
- Use Tailwind for styling (no custom CSS)
- Test with Clerk auth before committing
- Always create migrations (never modify production DB directly)
- Migrations must be idempotent (safe to run multiple times)
- Test migrations locally before pushing
- Commit migration files to version control
Solution:
cd backend
cp .env.sample .env
# Edit .env and add your DATABASE_URLSolution:
# Change port in backend/.env
PORT=3001
# Or kill existing process on port 3000
lsof -i :3000
kill -9 <PID>Solution:
cd backend
pnpm prisma generateSolution:
cd backend
# Reset database (dev only!)
pnpm prisma migrate reset
# Then reapply migrations
pnpm prisma migrate devSolution:
# Check backend is running
curl http://localhost:3000/health
# If not, start backend:
cd backend && pnpm run dev
# Verify FRONTEND_URL in backend/.env
FRONTEND_URL=http://localhost:5173Solution:
# Backend: Restart tsx watch
# Terminal 1: Ctrl+C
# Terminal 1: pnpm run dev
# Frontend: Clear browser cache
# Hard refresh: Cmd+Shift+R (Mac) or Ctrl+Shift+R (Windows)Solution:
cd backend
# Ensure test database exists
createdb cvpilot_test
# Or use docker
docker run -d \
--name postgres-test \
-e POSTGRES_PASSWORD=postgres \
-p 5433:5432 \
postgres:15
# Set DATABASE_URL to test DB
export DATABASE_URL="postgresql://postgres:postgres@localhost:5433/cvpilot_test"
# Run tests
pnpm run testCauses & Solutions:
- No caching → Implement Redis cache or in-memory LRU
- Cold Gemini API → First call is slower; subsequent calls cached
- Large job list → Filter jobs before LLM scoring (use Active Jobs filters)
- Slow DB → Check database indexes on job_matches table
# Check database query performance
cd backend
pnpm prisma studio
# Or view logs
DEBUG=prisma:* pnpm run dev- Backend README — Setup, architecture, running instructions
- API Documentation — Complete REST API reference (1200+ lines)
- Architecture Overview — Layered design patterns explained
- V2 Migration Guide — Job matching V2 rollout strategy
- React Documentation — Official React docs and patterns
- Prisma Docs — ORM setup, migrations, best practices
- TypeScript Handbook — Type system reference
- Clerk Documentation — Auth integration guide
- Tailwind CSS — Utility-first CSS framework
- GitHub Issues — Bug reports, feature requests
- GitHub Discussions — Ask questions, share ideas
- GitHub Projects — Development roadmap
Before submitting a PR, ensure:
- TypeScript compiles without errors (
pnpm run build) - No type assertions with
any - No unused imports or variables
- Code follows architecture patterns
- Error handling is comprehensive
- Logging includes context (userId, duration, etc.)
- Unit tests added for service functions
- Integration tests added for API endpoints
- All tests pass (
pnpm run test:all) - Test coverage for happy & error paths
- Code comments explain complex logic
- Function JSDoc comments added
- README updated (if needed)
- API docs updated (if endpoints changed)
- Commit messages follow conventional format
- Migrations created (if schema changed)
- Migrations tested locally
- Migration files committed to Git
- Rollback plan documented (if applicable)
- No secrets in code or logs
- Input validation with Zod
- SQL injection impossible (using Prisma)
- Auth checks in place
- CORS properly configured
- No N+1 queries (check Prisma includes)
- API responses under 2 seconds
- Database queries optimized
- Unnecessary API calls removed
- Caching strategy documented
This project is proprietary and confidential. All rights reserved.
Unauthorized copying, modification, distribution, or use of this project is strictly prohibited.
For licensing inquiries, contact: backend@cvpilot.dev
CVPilot was built with ❤️ using:
- Google Gemini API — Advanced AI for resume parsing and job matching
- Clerk — Secure authentication infrastructure
- Cloudinary — Reliable file storage and CDN
- Prisma — Type-safe database access
- React & Vite — Modern, fast frontend development
- Express & Node.js — Lightweight, scalable backend
Have questions or need help?
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: backend@cvpilot.dev
If CVPilot helped you, please consider giving us a ⭐ on GitHub!
Made with ❤️ by the CVPilot Team
Last Updated: April 2026 | Version 1.1.0