Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tech-dipesh/yeti-Jobs/llms.txt

Use this file to discover all available pages before exploring further.

Yeti Jobs is structured as a classic layered PERN architecture where each tier has a single, well-defined responsibility. The React client handles all rendering and user interaction. Requests flow through versioned Express API routes, pass through a middleware chain, and are processed by a service layer before reaching PostgreSQL. File uploads bypass the API and land directly in Supabase Storage, with the returned public URL persisted in the database. Background work — job expiry, email dispatch — is handled by dedicated services that run alongside the main HTTP server.

System Overview

┌─────────────────────────────────────────────────────────────┐
│                        CLIENT LAYER                         │
│  React 19 + Vite  │  TailwindCSS  │  React Router 7         │
│  Axios (API calls) │  Context API (auth state)              │
└───────────────────────────┬─────────────────────────────────┘
                            │  HTTP / JSON (cookies for JWT)

┌─────────────────────────────────────────────────────────────┐
│                       API LAYER  (/api/v1)                  │
│  Express 5  │  Helmet  │  CORS  │  Rate Limit               │
│  Cookie-Parser  │  Multer (file upload)                     │
│                                                             │
│  Routes:                                                    │
│    /jobs          /users        /companies                  │
│    /applications  /admin        /notifications              │
│    /health        /swagger                                  │
│                                                             │
│  Middleware Chain:                                          │
│    isLoggedIn → isJobSeeker | isEmployee | isAdmin          │
│    Zod validation schemas                                   │
└──────────┬───────────────────────────┬──────────────────────┘
           │                           │
           ▼                           ▼
┌──────────────────────┐   ┌───────────────────────────────────┐
│    SERVICE LAYER     │   │       BACKGROUND SERVICES         │
│  Business logic,     │   │  node-cron (midnight job expiry)  │
│  pdf-parse,          │   │  Nodemailer (email verification,  │
│  Groq AI (ATS score) │   │    password reset)                │
└──────────┬───────────┘   └───────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                     DATABASE LAYER                          │
│  PostgreSQL 15  │  pg driver (connection pool)              │
│  10 tables  │  GIN full-text index  │  Triggers  │  Enums   │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                  EXTERNAL STORAGE & SERVICES                │
│  Supabase Storage (résumés, profile pictures, logos)        │
│  Supabase PostgreSQL (production database host)             │
└─────────────────────────────────────────────────────────────┘

Layer Reference

React Frontend

The client is a single-page application built with React 19 and bundled by Vite 7. TailwindCSS 4 handles all styling with a utility-first approach, and React Router 7 manages client-side routing with lazy-loaded routes using React.lazy() and Suspense to keep the initial bundle small. State management relies on the Context API — a central useAuth context stores the verified status, logged-in flag, and user role so that every component can read authentication state without prop drilling or redundant network calls. HTTP communication is handled exclusively through Axios, configured with the VITE_SERVER_URL base URL. Cookies are sent with every request (withCredentials: true) so the JWT stored in the HTTP-only cookie is included automatically. Client-side RBAC enforces route guards before the server is ever reached:
  • Guests cannot visit recruiter or admin pages.
  • Recruiters cannot access job-seeker–specific pages or admin dashboards.
  • Admins have broader access but are still restricted from guest and recruiter action pages.
Key frontend libraries: react-router 7.13.1, axios 1.13.5, react-toastify 11.0.5, react-select 5.10.2, react-phone-number-input 3.4.16, @vercel/analytics, @vercel/speed-insights.

Express API — MVC Backend

The backend is an Express 5 application structured with a strict MVC pattern. All routes are prefixed with /api/v1 for versioning, making it straightforward to introduce a /api/v2 namespace without breaking existing clients. Middleware chain (applied in order):
  1. cors — whitelists only CLIENT_BASE_URL; blocks all other origins.
  2. express-rate-limit — 200 requests per minute globally; 2 requests per minute on password-reset routes.
  3. helmet — strips X-Powered-By, adds security response headers (CSP, HSTS, X-Frame-Options, etc.).
  4. express.json() — parses JSON request bodies.
  5. cookie-parser — makes the JWT cookie available as req.cookies.
  6. authUserMiddleware (isLoggedIn) — verifies the JWT on protected route groups.
  7. isAdminMiddleware — additional role check applied before the /admin route group.
  8. multer — handles multipart/form-data for résumé and profile-picture uploads.
Route groups:
PrefixAuth requiredRole guard
/api/v1/jobsPartialPublic read, auth for write
/api/v1/usersPartialPublic for login/signup
/api/v1/companiesYesisLoggedIn
/api/v1/applicationsYesisLoggedIn
/api/v1/adminYesisLoggedIn + isAdmin
/api/v1/notificationsYesisLoggedIn
/api/v1/healthNoNone
/api/v1/swaggerNoNone
Validation is applied at three levels — Zod schemas on the server, client-side form validation, and database-level constraints — so that invalid data cannot persist even if the upper layers are bypassed. The global error handler returns consistent JSON shapes: { message: string } with appropriate 2xx, 4xx, or 5xx status codes.

PostgreSQL Database

The database uses raw SQL through the pg connection pool — no ORM. This gives full control over query plans, index usage, and schema design. Schema: 10 tables
TablePurpose
usersAll user accounts (job seekers, recruiters, admins)
companiesCompany profiles
jobsJob listings with tsvector full-text search column
applicationsJob applications with status tracking
saved_jobsBookmarked jobs per user
user_companies_followsCompany follow relationships
email_verifiedEmail verification and password-reset tokens
ats_scoreAI résumé scoring results (JSONB feedback)
user_educationUser education history
notificationsPer-user notification events
PostgreSQL features in use:
  • Enums at the database level for role, education, job_type, application_status, and email_verification_type — invalid values are rejected before they reach application code.
  • GIN index on jobs.search_title (a tsvector column) for performant full-text job search, replacing slow ILIKE prefix scans.
  • B-tree indexes on companies.name and email_verified.verified_code for fast lookups on the most queried columns.
  • Trigger on jobs that auto-populates the search_title tsvector column on every INSERT or UPDATE, keeping the search index current without application-level intervention.
  • ON DELETE CASCADE — deleting a user automatically removes their email verifications, follows, ATS scores, and notifications.
  • ON DELETE RESTRICT — tables where linked data must be explicitly cleared before the parent record can be deleted.
  • SELECT EXISTS(SELECT 1 ...) pattern for condition checks — returns a boolean without fetching any rows.
  • Pagination on all list endpoints to bound response payload size.
Queries are reviewed with EXPLAIN ANALYZE to catch slow plans before reaching production.

Supabase — Storage and Hosted Database

Supabase serves two distinct roles in Yeti Jobs. 1. File storage — résumés, profile pictures, and company logos are uploaded via the @supabase/supabase-js SDK directly into Supabase Storage buckets. After a successful upload the SDK returns a public URL which is saved to the corresponding column in PostgreSQL (users.resume_url, users.profile_pic_url, companies.logo_url). The Express server acts as a proxy: Multer holds the file in memory, the service layer streams it to Supabase, and the URL is persisted atomically with the profile update. 2. Production database host — in production the PostgreSQL database is hosted on Supabase. The backend connects through Supabase’s IPv6-compatible connection pooler using the URL_SUPABASE_CONNECT string and authenticates with ANON_KEY_SUPABASE. The SSL option { rejectUnauthorized: false } is enabled to allow inbound connections from Render’s network. Configuration env vars:
URL_SUPABASE_CONNECT=https://your-project-id.supabase.co
ANON_KEY_SUPABASE=your_supabase_anon_key

Third-Party Integrations

Groq AI — ATS Résumé Scoring Résumé files uploaded by job seekers are parsed with pdf-parse to extract plain text. That text is sent to the Groq API (GROK_API) via the openai SDK (Groq exposes an OpenAI-compatible interface). The API returns a numeric ATS score and structured feedback which are stored in the ats_score table as score (integer) and feedback (JSONB). Nodemailer — Transactional Email Email verification codes and password-reset tokens are delivered via Nodemailer using SMTP. Configuration requires three env vars:
NODEMAILER_MY_EMAIL=you@example.com
NODEMAILER_MY_PASSWORD=your_app_password
NODEMAILER_MY_HOST=smtp.gmail.com
Before initiating an SMTP connection, the server validates the recipient’s email domain using Node’s built-in dns/promises module — this prevents unnecessary SMTP round-trips for addresses with non-existent domains. The password-reset route is strictly rate-limited to 2 requests per minute to prevent abuse. node-cron — Scheduled Job Expiry A cron task registered in src/services/cron-task.js fires every night at midnight (00:00). It executes a single UPDATE query that sets is_job_open = 'closed' on all jobs rows where created_at is older than 30 days and the job is still marked open. The number of affected rows is logged after each run. Because the update is a single query it does not hold a table lock for a meaningful duration even at scale.
Schedule:   0 0 * * *   (every day at 00:00)
Action:     UPDATE jobs SET is_job_open = 'closed'
            WHERE created_at < NOW() - INTERVAL '30 days'
            AND is_job_open = 'active'
Swagger UI Interactive API documentation is served from /api/v1/swagger using swagger-ui-express and a YAML spec loaded with yamljs. This provides a live, explorable reference for all backend endpoints including request parameters, body schemas, and response formats.

Security Architecture

The platform applies defence-in-depth across every layer.
ControlImplementation
AuthenticationJWT in HTTP-only cookie, signed with JSON_SECRET_KEY, configurable expiry via MAXAGE
AuthorisationRBAC enforced at client route guards, Express middleware, and PostgreSQL constraints
Rate limiting200 req/min globally; 2 req/min on password-reset via express-rate-limit
Security headershelmet (CSP, HSTS, X-Frame-Options, and more)
CORSOnly CLIENT_BASE_URL is whitelisted
Input validationZod schemas server-side + database-level enums and constraints
Password hashingbcryptjs
File uploadmulter guards on file field name; unsupported field names return 401

Deployment Topology

Internet

   ├──▶ Vercel (React SPA)  ──────────────────────▶ Render (Express API)
   │         VITE_SERVER_URL                              │
   │                                                      ├──▶ Supabase PostgreSQL
   │                                                      └──▶ Supabase Storage

   └──▶ Render /api/v1/swagger  (Swagger UI, public)
  • Frontend — deployed to Vercel; every push to main triggers an automatic redeploy. Set VITE_SERVER_URL in the Vercel dashboard.
  • Backend — deployed as a Web Service on Render. All backend env vars are configured in the Render dashboard. The Render free tier sleeps after 15 minutes of inactivity; a keep-alive cron ping runs every 15 minutes to prevent cold starts.
  • Database & Storage — both hosted on Supabase. The backend connects via the connection pooler URL with SSL enabled.

Build docs developers (and LLMs) love