Database Schema
The database is designed with Prisma. Below are the key models and their relationships.
User
model User {
id String @id @default(cuid())
name String?
email String @unique
passwordHash String?
role Role
phone String?
authProvider String?
authProviderId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
jobSeekerProfile JobSeekerProfile?
employerProfile EmployerProfile?
postedJobs Job[] @relation("PostedJobs")
applications Application[] @relation("ApplicantApplications")
savedJobs SavedJob[]
passwordResetToken String? @unique
passwordResetTokenExpiry DateTime?
@@unique([authProvider, authProviderId])
}Job
model Job {
id String @id @default(cuid())
title String
location String
description String @db.Text
requirements String? @db.Text
employmentType EmploymentType @default(FULLTIME)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
employerId String
employer User @relation("PostedJobs", fields: [employerId], references: [id], onDelete: Cascade)
companyId String
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
applications Application[]
savedBy SavedJob[]
}Company
model Company {
id String @id @default(cuid())
name String
website String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
jobs Job[]
employers EmployerProfile[]
@@unique([name, website])
}EmployerProfile
Links a User to a Company. The `userId` is unique, enforcing a one-to-one relationship.
model EmployerProfile {
id String @id @default(cuid())
userId String @unique
companyId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
}JobSeekerProfile & Sub-Models
Contains all details for a job seeker's resume.
model JobSeekerProfile {
id String @id @default(cuid())
userId String @unique
summary String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
skills Skill[]
experiences Experience[]
educations Education[]
}
model Skill {
id String @id @default(cuid())
name String
level String?
profileId String
profile JobSeekerProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
}
model Experience {
id String @id @default(cuid())
company String
title String
startDate DateTime
endDate DateTime?
details String? @db.Text
profileId String
profile JobSeekerProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
}
model Education {
id String @id @default(cuid())
school String
degree String
field String?
startDate DateTime
endDate DateTime?
profileId String
profile JobSeekerProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
}
Authentication API
POST /api/auth/register
Registers a new user. Body must match `RegisterInput` schema.
POST /api/auth/login
Logs in a user with email/password. Returns a JWT cookie.
POST /api/auth/sso/status
Checks if an SSO user exists. Returns `{ status: 'NEW_USER' | 'COMPLETE' }`.
POST /api/auth/sso-login
Finalizes SSO login/registration after role selection for new users.
POST /api/auth/logout
Clears the user's session cookie.
Users API
PUT /api/users/me
Updates the name and phone number for the currently authenticated user. (All Roles)
Jobs API
GET /api/jobs
Fetches a list of all jobs. Supports `search` and `location` query params. (Public)
GET /api/jobs/:id
Fetches details for a single job. (Public)
POST /api/jobs
Creates a new job posting. (Employer only)
PUT /api/jobs/:id
Updates a job posting. (Employer only)
DELETE /api/jobs/:id
Deletes a job posting. (Employer only)
Companies API
GET /api/companies/search
Searches for companies by name. (Employer only)
POST /api/companies
Creates a new company profile. (Employer only)
Applications API
POST /api/jobs/:jobId/apply
Submits an application for a job. (Job Seeker only)
GET /api/applications/my-applications
Fetches all applications for the logged-in user. (Job Seeker only)
GET /api/applications/:applicationId/profile
Securely fetches an applicant's profile. (Employer only)
PUT /api/applications/:applicationId/status
Updates the status of an application. (Employer only)
Profiles API
GET /api/profile/my-profile
Fetches the full profile of the logged-in user. (Job Seeker only)
GET /api/employer-profiles/my-profile
Fetches the company profile of the logged-in user. (Employer only)
Uploads API
POST /api/upload/resume
Uploads a resume file. Returns a relative URL. (Job Seeker only)
Job Seeker Flow
1. Registration & Profile Setup
- User signs up via the form (`POST /api/auth/register`) or SSO.
- For SSO, if the user is new, they are prompted to select the "Job Seeker" role.
- After registration, they are redirected to `/profile`.
- On their profile page, they can update their name/phone (`PUT /api/users/me`), summary (`PUT /api/profile/my-profile`), and add skills, experience, etc. via their respective endpoints.
2. Applying for a Job
- User browses jobs (`GET /api/jobs`) and clicks on a listing to view details (`GET /api/jobs/:id`).
- On the details page, the frontend checks if the user has already applied or saved this job (`GET /api/jobs/:id/status`).
- User clicks "Apply Now", which opens a modal.
- Frontend sends the resume file to `POST /api/upload/resume`, which returns a relative URL.
- Frontend sends the resume URL and cover letter to `POST /api/jobs/:jobId/apply`.
- User is redirected to `/my-applications` to see their submission.
Employer Flow
1. Registration & Company Setup
- User registers with the "Employer" role and is redirected to `/dashboard/company-setup`.
- They can search for an existing company (`GET /api/companies/search?name=...`).
- If they select a company, the frontend links their profile (`POST /api/employer-profiles`).
- If they create a new company, the frontend first creates it (`POST /api/companies`), then links their profile.
- User is redirected to their main dashboard.
2. Managing Jobs & Applicants
- Employer views their jobs on the dashboard (`GET /api/jobs/my-jobs`).
- They can post a new job (`POST /api/jobs`) or edit an existing one (`PUT /api/jobs/:id`).
- They click "View Applicants" on a job posting.
- Frontend fetches all applicants for that job (`GET /jobs/:jobId/applications`).
- From the list, they can view an applicant's resume (direct link) or view their full profile.
- Clicking "View Profile" navigates to the applicant's profile page, which fetches data from the secure endpoint (`GET /api/applications/:applicationId/profile`).
- They can update an application's status directly from the list (`PUT /api/applications/:applicationId/status`).