A secure, stateless REST API backend built with Java 17, Spring Boot 3.3.2, and MongoDB to help job seekers track, filter, organize, and manage their job applications and recruitment pipelines.
During a job search, candidates apply to dozens of roles across different companies, portals, and platforms (LinkedIn, referral, career pages). Keeping track of different stages (Online Assessments, Technical Rounds, HR Interviews), scheduled dates, custom recruitment pipelines, application statuses, and notes in spreadsheets is static, error-prone, and lacks real-time integrations.
The objective of JobTrack is to provide a unified, secure, and user-scoped tracking tool that:
- Secures candidate data with stateless token-based authorization.
- Isolates data so users can only access their own applications.
- Accommodates dynamic company-specific interview pipelines (custom stages).
- Enables search, filtering, and sorting to organize applications.
- Provides statistics and conversion rates (interview rates, offer rates).
- Stateless JWT Authentication: Secure user registration, password hashing (BCrypt), and stateless session JWT logins.
- Core CRUD Scoped to User: Add, read, update, and delete job applications with strict ownership boundaries.
- Recruitment Lifecycle & Stage Management:
- Creates a default 5-stage pipeline (
Applied->OA->Technical Interview->HR Interview->Offer) if no custom stages are supplied. - Allows defining custom stage pipelines during application creation.
- Supports adding, editing, and deleting custom stages.
- Stage transition tracking: marking current stages, skipping intermediate rounds, and completing pipelines.
- Creates a default 5-stage pipeline (
- Search, Filter & Paginate:
- Case-insensitive keyword search matching company name or role.
- Filters by status (
ACTIVE,REJECTED,WITHDRAWN), current stage, source, and applied date ranges. - Supports custom sorting properties and directions.
- Statistics Dashboard:
- Tracks counts for total, active, rejected, and withdrawn applications.
- Tracks current application distribution across stages.
- Computes and rounds interview rates and offer rates to exactly 2 decimal places.
- Language: Java 17 (OpenJDK 17)
- Framework: Spring Boot 3.3.2
- Spring Security: Authentication & Authorization (stateless JWT)
- Spring Data MongoDB: Object-Document Mapping (ODM)
- Jakarta Validation: Request validation constraints
- Database: MongoDB (Local or Atlas)
- Libraries:
- io.jsonwebtoken (jjwt): Token signature checks
- Springdoc OpenAPI: Interactive Swagger UI documentation
- Testing: JUnit 5, Mockito, Spring Security Test, MockMvc
- Core: React 19 (TypeScript)
- Build Tool: Vite 8, PostCSS
- Styling: Tailwind CSS v4
- HTTP Client: Axios (configured with token request interceptors & auth event listeners)
- Routing: React Router v7 (SPA routing)
Job-Tracker/
├── src/ # Spring Boot backend source files
│ ├── main/
│ │ ├── java/com/jobtrack/
│ │ │ ├── config/ # Security, CORS, & Swagger configurations
│ │ │ ├── controller/ # REST endpoints (Auth, Application, Stats, Health)
│ │ │ ├── dto/ # Request & Response DTO records
│ │ │ ├── exception/ # Custom Exceptions & GlobalExceptionHandler
│ │ │ ├── model/ # Database Models (User, JobApplication)
│ │ │ ├── repository/ # Spring Data repositories
│ │ │ ├── security/ # Custom filter & UserDetails implementation
│ │ │ └── service/ # Core business logic services
│ │ └── resources/
│ │ └── application.yml # Environment-based YAML config
│ └── test/ # JUnit 5 integration & controller MockMvc tests
├── frontend/ # React Vite TypeScript frontend files
│ ├── src/ # React code (pages, components, api clients, context)
│ ├── public/ # Public assets (briefcase SVG favicon)
│ ├── vercel.json # Client-side SPA routing rewrites for Vercel
│ ├── package.json # Frontend package dependencies
│ ├── .env.example # Frontend environment configuration template
│ └── .gitignore # Exclusions list for node modules and env secrets
├── Dockerfile # Multi-stage Docker config for Render
├── .dockerignore # Docker folder exclusions
├── .env.example # Root env variables template
├── .gitignore # Root git ignore rules
├── pom.xml # Maven POM dependency map
└── README.md # Documentation
graph TD
Browser[Client Browser] -->|http://localhost:5173| Frontend[Vite React Frontend]
Frontend -->|http://localhost:8081/api| Backend[Spring Boot Backend]
Backend -->|mongodb://localhost:27017| LocalDB[(Local MongoDB)]
graph TD
Browser[Client Browser] -->|HTTPS| Frontend[Vercel Hosted Client]
Frontend -->|HTTPS REST API| Backend[Render Hosted Container]
Backend -->|Atlas Driver Protocol| CloudDB[(MongoDB Atlas Cluster)]
- Register: Client registers with
name,email, andpassword. The API checks for duplicate emails, hashes the password via BCrypt, and saves aUserdocument. - Login: Client logins with
emailandpassword. The API matches credentials, generates a HS256-signed JWT token, and returns it with a200 OK. - Secure Request: The client attaches
Authorization: Bearer <token>to headers. TheJwtAuthenticationFilterinterceptor validates the token, extracts the user ID, loads the user details principal, and loads them into the security context.
- Each
JobApplicationmaintains an ordered list ofApplicationStagesub-documents. - Default Pipeline:
[1] Applied (CURRENT) -> [2] OA (PENDING) -> [3] Technical Interview (PENDING) -> [4] HR Interview (PENDING) -> [5] Offer (PENDING) - Transitions: Advancing to a stage updates the target to
CURRENTand marks the previous stage asCOMPLETED. Any intermediate stages between the old and new stage order that werePENDINGare marked asSKIPPED(e.g. going fromAppliedtoTechnical Interviewautomatically marksOAasSKIPPED). - Outcome updates: The status of an application can be set to
ACTIVE,REJECTED, orWITHDRAWNvia a dedicated endpoint, which changes the top-level application outcome without altering the stage pipeline state.
| Method | Endpoint | Purpose | Auth Required |
|---|---|---|---|
| POST | /api/auth/register |
Register a new user account | No |
| POST | /api/auth/login |
Log in and retrieve JWT Bearer token | No |
| GET | /api/users/me |
Fetch authenticated user profile details | Yes |
| GET | /api/health |
Public service health check status | No |
| POST | /api/applications |
Create a new job application | Yes |
| GET | /api/applications |
List, search, filter, sort, and paginate applications | Yes |
| GET | /api/applications/{id} |
Fetch full details of a specific job application | Yes |
| PUT | /api/applications/{id} |
Update basic details of a job application | Yes |
| DELETE | /api/applications/{id} |
Delete a job application | Yes |
| PATCH | /api/applications/{id}/outcome |
Update application outcome status | Yes |
| PATCH | /api/applications/{id}/stage |
Transition active stage to target stage | Yes |
| POST | /api/applications/{id}/stages |
Add a new custom stage to the application | Yes |
| PATCH | /api/applications/{id}/stages/{stageId} |
Update details of a specific stage | Yes |
| DELETE | /api/applications/{id}/stages/{stageId} |
Delete a custom stage from the application | Yes |
| GET | /api/applications/stats |
Retrieve job-search statistics and rates | Yes |
- Create a
.envfile in the project root directory:MONGODB_URI=mongodb://localhost:27017 MONGODB_DATABASE=jobtrack JWT_SECRET=403b472b535d4e138a0fdfebadcf626788b7762671239aa8de6739bcbcad2341 JWT_EXPIRATION=86400000 PORT=8081 FRONTEND_URL=http://localhost:5173
- Start MongoDB Server locally on port
27017. - Launch the Spring Boot backend using Maven CLI:
(Or press
mvn spring-boot:run
F5in VS Code to load the local configurations automatically)
- Create a
.envfile in thefrontend/directory:VITE_API_BASE_URL=http://localhost:8081/api - Navigate to the
frontend/folder, install dependencies, and start the development server:cd frontend npm install npm run dev - Open
http://localhost:5173in your browser.
- Swagger API Docs: Navigate to
http://localhost:8081/swagger-ui/index.html(when backend is running). - Run Backend Tests:
mvn clean test(16 controller mock MVC integration tests executed). - Verify Frontend Bundle:
npm run buildinside thefrontend/directory.
- Create a free M0 cluster on MongoDB Atlas.
- In Database Access, create a user (e.g.
bingumallagreeshmitha_db_user) with read/write access. - In Network Access, add IP
0.0.0.0/0(Allow Access from Anywhere) to permit Render server bindings. - Copy your connection string from the Connect -> Drivers popup, URL-encode any special characters in your password (e.g.
@as%40), and set it as yourMONGODB_URIenvironment variable.
- Create a new Web Service on Render, and link your GitHub repository.
- Select runtime Docker (Render will build the application using the root
Dockerfile). - Under Advanced, configure:
- Health Check Path:
/api/health - Environment Variables:
MONGODB_URI=mongodb+srv://<username>:<password>@cluster0.mongodb.net/?appName=Cluster0MONGODB_DATABASE=jobtrackJWT_SECRET= (Generate a secure random key)JWT_EXPIRATION=86400000FRONTEND_URL=https://<your-vercel-domain>.vercel.app
- Health Check Path:
- Click Deploy Web Service and copy the live URL (e.g.,
https://jobtrack-backend.onrender.com).
- Import your GitHub repository on Vercel.
- Set the Root Directory setting to the
frontenddirectory. - Add the following Environment Variable:
VITE_API_BASE_URL=https://<your-render-backend-url>.onrender.com/api
- Click Deploy. Vercel will compile the React bundle and deploy it. Since
vercel.jsonrewrite settings are loaded, client routing is managed automatically.
- POST
/api/auth/register - Body:
{ "name": "Greeshmitha", "email": "greeshmitha@example.com", "password": "Password123" } - Response (
201 Created):{ "id": "64d0be036577312389ab4101", "name": "Greeshmitha", "email": "greeshmitha@example.com", "createdAt": "2026-08-15T22:30:00" }
- POST
/api/auth/login - Body:
{ "email": "greeshmitha@example.com", "password": "Password123" } - Response (
200 OK):{ "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI2NGQwYm...", "tokenType": "Bearer", "expiresIn": 86400000, "user": { "id": "64d0be036577312389ab4101", "name": "Greeshmitha", "email": "greeshmitha@example.com", "createdAt": "2026-08-15T22:30:00" } }
- POST
/api/applications - Headers:
Authorization: Bearer <token> - Body:
{ "company": "Google", "role": "Software Engineer", "location": "Bangalore", "source": "REFERRAL", "appliedDate": "2026-08-15", "jobUrl": "https://careers.google.com/jobs/123", "notes": "Referred by senior dev", "stages": [ { "name": "Applied", "order": 1 }, { "name": "Technical Assessment", "order": 2 }, { "name": "System Design Interview", "order": 3 }, { "name": "Offer", "order": 4 } ] } - Response (
201 Created):{ "id": "64d0c1026577312389ab4102", "company": "Google", "role": "Software Engineer", "location": "Bangalore", "source": "REFERRAL", "appliedDate": "2026-08-15", "applicationStatus": "ACTIVE", "currentStage": { "id": "stage-uuid-1", "name": "Applied", "type": "CUSTOM", "status": "CURRENT", "order": 1, "scheduledAt": null, "completedAt": null, "notes": null }, "stages": [ { "id": "stage-uuid-1", "name": "Applied", "status": "CURRENT", "order": 1 }, { "id": "stage-uuid-2", "name": "Technical Assessment", "status": "PENDING", "order": 2 }, { "id": "stage-uuid-3", "name": "System Design Interview", "status": "PENDING", "order": 3 }, { "id": "stage-uuid-4", "name": "Offer", "status": "PENDING", "order": 4 } ], "jobUrl": "https://careers.google.com/jobs/123", "notes": "Referred by senior dev", "createdAt": "2026-08-15T22:31:00", "updatedAt": "2026-08-15T22:31:00" }
- PATCH
/api/applications/64d0c1026577312389ab4102/stage - Headers:
Authorization: Bearer <token> - Body:
{ "stageId": "stage-uuid-3" } - Response (
200 OK):{ "id": "64d0c1026577312389ab4102", "company": "Google", "role": "Software Engineer", "applicationStatus": "ACTIVE", "currentStage": { "id": "stage-uuid-3", "name": "System Design Interview", "status": "CURRENT", "order": 3 }, "stages": [ { "id": "stage-uuid-1", "name": "Applied", "status": "COMPLETED", "completedAt": "2026-08-15T22:32:00", "order": 1 }, { "id": "stage-uuid-2", "name": "Technical Assessment", "status": "SKIPPED", "order": 2 }, { "id": "stage-uuid-3", "name": "System Design Interview", "status": "CURRENT", "order": 3 }, { "id": "stage-uuid-4", "name": "Offer", "status": "PENDING", "order": 4 } ] }
- GET
/api/applications?search=Google&status=ACTIVE - Headers:
Authorization: Bearer <token> - Response (
200 OK):{ "content": [ { "id": "64d0c1026577312389ab4102", "company": "Google", "role": "Software Engineer", "location": "Bangalore", "source": "REFERRAL", "appliedDate": "2026-08-15", "applicationStatus": "ACTIVE", "currentStage": { "id": "stage-uuid-3", "name": "System Design Interview", "status": "CURRENT", "order": 3 } } ], "page": 0, "size": 10, "totalElements": 1, "totalPages": 1, "first": true, "last": true }
- GET
/api/applications/stats - Headers:
Authorization: Bearer <token> - Response (
200 OK):{ "totalApplications": 1, "activeApplications": 1, "rejectedApplications": 0, "withdrawnApplications": 0, "stageCounts": { "System Design Interview": 1 }, "interviewRate": 100.0, "offerRate": 0.0 }
- Audit logs: Track exact histories of application status changes and user modifications.
- Calendar integration: Synchronize interview stage dates with external services (like Google Calendar).
- Resume parsing: Enable users to attach resume files and automatically extract details to pre-populate application fields.