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

  1. User signs up via the form (`POST /api/auth/register`) or SSO.
  2. For SSO, if the user is new, they are prompted to select the "Job Seeker" role.
  3. After registration, they are redirected to `/profile`.
  4. 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

  1. User browses jobs (`GET /api/jobs`) and clicks on a listing to view details (`GET /api/jobs/:id`).
  2. On the details page, the frontend checks if the user has already applied or saved this job (`GET /api/jobs/:id/status`).
  3. User clicks "Apply Now", which opens a modal.
  4. Frontend sends the resume file to `POST /api/upload/resume`, which returns a relative URL.
  5. Frontend sends the resume URL and cover letter to `POST /api/jobs/:jobId/apply`.
  6. User is redirected to `/my-applications` to see their submission.

Employer Flow

1. Registration & Company Setup

  1. User registers with the "Employer" role and is redirected to `/dashboard/company-setup`.
  2. They can search for an existing company (`GET /api/companies/search?name=...`).
  3. If they select a company, the frontend links their profile (`POST /api/employer-profiles`).
  4. If they create a new company, the frontend first creates it (`POST /api/companies`), then links their profile.
  5. User is redirected to their main dashboard.

2. Managing Jobs & Applicants

  1. Employer views their jobs on the dashboard (`GET /api/jobs/my-jobs`).
  2. They can post a new job (`POST /api/jobs`) or edit an existing one (`PUT /api/jobs/:id`).
  3. They click "View Applicants" on a job posting.
  4. Frontend fetches all applicants for that job (`GET /jobs/:jobId/applications`).
  5. From the list, they can view an applicant's resume (direct link) or view their full profile.
  6. Clicking "View Profile" navigates to the applicant's profile page, which fetches data from the secure endpoint (`GET /api/applications/:applicationId/profile`).
  7. They can update an application's status directly from the list (`PUT /api/applications/:applicationId/status`).