A full-stack job portal web application with role-based access for Candidates, Employers, and Admins. Built with React, Node.js, Express, and PostgreSQL.
| Layer | Technology |
|---|---|
| Frontend | React 19 (Vite) + Tailwind CSS |
| Backend | Node.js + Express.js |
| Database | PostgreSQL |
| Auth | JWT (access token + refresh token via httpOnly cookie) |
Candidate:
- Browse jobs with search, location filter, type filter, and pagination
- View full job details and apply with one click
- Track application status (applied → reviewed → accepted/rejected)
- Edit profile (name, skills, resume URL, bio)
Employer:
- Post, edit, and delete job listings
- View applicants for each listing with full profile info
- Update applicant status via inline dropdown
Admin:
- View all registered users
- Delete any job listing
Auth & Security:
- JWT access token (15min, in-memory) + refresh token (7d, httpOnly cookie)
- Passwords hashed with bcrypt (10 salt rounds)
- Role-based route protection on both frontend and backend
- CORS origin configurable via environment variable
┌──────────────┐ ┌──────────────┐
│ users │ │ profiles │
├──────────────┤ ├──────────────┤
│ id (PK) │──1:1──│ id (PK) │
│ name │ │ user_id (FK) │
│ email (UQ) │ │ skills │
│ password_hash│ │ resume_url │
│ role │ │ bio │
│ created_at │ └──────────────┘
└──────┬───────┘
│
│1:N 1:N
▼ ▼
┌──────────────┐ ┌────────────────┐
│ jobs │ │ applications │
├──────────────┤ ├────────────────┤
│ id (PK) │──1:N──│ id (PK) │
│ employer_id │ │ candidate_id │
│ title │ │ job_id (FK) │
│ description │ │ status │
│ location │ │ applied_at │
│ type │ │ UQ(cand, job) │
│ deadline │ └────────────────┘
│ created_at │
└──────────────┘
/client → React frontend (Vite)
/src
/components → Navbar, ProtectedRoute
/context → AuthContext (session management)
/pages → All page components
/utils → api.js (fetch wrapper with 401 retry)
/server → Express backend
/config → db.js, init.sql, seed.sql
/controllers → Business logic per feature
/middleware → JWT auth middleware
/models → PostgreSQL queries (parameterized)
/routes → API endpoint definitions
server.js → Entry point
.env.example → Environment variable template
- Node.js 18+
- PostgreSQL 14+
git clone https://github.com/yourusername/job-portal.git
cd job-portalcp .env.example .env
# Edit .env with your PostgreSQL credentials and secretspsql -U postgres -c "CREATE DATABASE jobportal;"
psql -U postgres -d jobportal -f server/config/init.sql
psql -U postgres -d jobportal -f server/config/seed.sqlSeed data: 1 admin, 2 employers, 3 candidates, 5 jobs, 6 applications. All seed users have password:
password123
| Role | |
|---|---|
| admin@jobportal.com | admin |
| hr@techcorp.com | employer |
| jobs@startupxyz.com | employer |
| alice@email.com | candidate |
| bob@email.com | candidate |
| charlie@email.com | candidate |
# Terminal 1 — Backend
cd server
npm install
npm run dev
# Terminal 2 — Frontend
cd client
npm install
npm run devThe backend runs on http://localhost:5000 and frontend on http://localhost:5173.
| Method | Route | Description | Auth Required |
|---|---|---|---|
| POST | /api/auth/register | Register new user | No |
| POST | /api/auth/login | Login, get tokens | No |
| POST | /api/auth/refresh | Refresh access token | Cookie |
| POST | /api/auth/logout | Clear refresh cookie | No |
| GET | /api/auth/me | Get current user | Yes |
| Method | Route | Description | Auth Required |
|---|---|---|---|
| GET | /api/jobs | List jobs (paginated, filterable) | No |
| GET | /api/jobs/:id | Get single job | No |
| POST | /api/jobs | Create a job listing | Employer |
| PUT | /api/jobs/:id | Update a job listing | Employer (owner) |
| DELETE | /api/jobs/:id | Delete a job listing | Employer/Admin |
| GET | /api/jobs/employer/me | Get employer's own listings | Employer |
Pagination: GET /api/jobs?page=1&limit=10&search=react&location=bangalore&type=full-time
Response includes: { success, data, pagination: { page, limit, total, pages } }
| Method | Route | Description | Auth Required |
|---|---|---|---|
| POST | /api/applications | Apply to a job | Candidate |
| GET | /api/applications/me | Get my applications | Candidate |
| GET | /api/applications/job/:jobId | Get applicants for a job | Employer (owner) |
| PUT | /api/applications/:id/status | Update application status | Employer (owner) |
| Method | Route | Description | Auth Required |
|---|---|---|---|
| GET | /api/profile | Get own profile | Yes |
| PUT | /api/profile | Update profile | Yes |
| Method | Route | Description | Auth Required |
|---|---|---|---|
| GET | /api/admin/users | List all users | Admin |
| DELETE | /api/admin/jobs/:id | Delete any job | Admin |
JWT Token Strategy:
- Access token → stored in JavaScript memory (not localStorage) — short-lived (15 min)
- Refresh token → stored in httpOnly, secure, sameSite cookie — long-lived (7 days)
- This mitigates XSS attacks since the refresh token is inaccessible to JavaScript
- On access token expiry, the frontend automatically attempts a silent refresh via the cookie
Known limitation: In-memory token is lost on page refresh, requiring a re-authentication via the refresh cookie. This is an intentional security tradeoff.
- Push to GitHub
- Connect repo to Railway or Render
- Set environment variables (
DATABASE_URL,JWT_SECRET,JWT_REFRESH_SECRET,CLIENT_URL) - Deploy — use
npm startas the start command
- Create a PostgreSQL instance on Railway or Supabase
- Run
init.sqland optionallyseed.sqlagainst the remote database - Copy the connection string to your
DATABASE_URLenv var
- Connect the
/clientdirectory to Vercel - Set the API URL in the frontend code or as an env var
- Deploy
Live Demo: [Add your deployed URL here]
Add screenshots of your running application here:
- Login page
- Job listings with search and filters
- Job detail page
- Candidate dashboard (application tracking)
- Employer dashboard (job management)
- Applicants view with status management
- Admin panel
- Email verification on registration
- File upload for resumes (S3/Cloudinary)
- Password reset via email
- WebSocket notifications for application status changes
- More granular admin controls (ban users, analytics)
- Unit and integration tests (Jest + Supertest)
- Rate limiting on auth endpoints
MIT