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 runs across three managed platforms in production: the React frontend is hosted on Vercel, the Express API is hosted on Render, and the PostgreSQL database plus file storage live on Supabase. This page walks through deploying each layer, connecting them together, and handling the quirks of free-tier hosting.

Frontend on Vercel

Vercel auto-detects Vite projects — no build configuration is required. The frontend/vercel.json contains a single rewrite rule that makes client-side React Router navigation work correctly by sending all requests to index.html.
frontend/vercel.json
{
  "rewrites": [
    {
      "source": "/(.*)",
      "destination": "/index.html"
    }
  ]
}
1

Import the repository

Go to vercel.com/new, click Add New Project, and import your GitHub repository. Vercel detects the frontend/ directory as the Vite app automatically.Set the following in the Vercel project configuration:
SettingValue
Root Directoryfrontend
Framework PresetVite (auto-detected)
Build Commandnpm run build
Output Directorydist
2

Add environment variables

In the Vercel dashboard go to Project → Settings → Environment Variables and add:
VariableValue
VITE_SERVER_URLYour Render backend URL, e.g. https://yeti-jobs.onrender.com
Vite embeds environment variables into the bundle at build time. If you update VITE_SERVER_URL in the Vercel dashboard, trigger a fresh deployment (Deployments → Redeploy) — the change is not picked up until the next build.
3

Deploy

Click Deploy. Every subsequent push to the main branch triggers an automatic redeployment. Pull-request branches get preview deployments at unique URLs automatically.

Backend on Vercel (alternative)

The backend also ships with a backend/vercel.json that enables deploying the Express API as Vercel Serverless Functions. When deployed this way, every inbound request is routed to src/app.js, which is the Express application entry point compiled with the @vercel/node runtime.
backend/vercel.json
{
  "builds": [
    { "src": "src/app.js", "use": "@vercel/node" }
  ],
  "routes": [
    { "src": "/(.*)", "dest": "src/app.js" }
  ]
}
Serverless deployments on Vercel do not maintain persistent connections between invocations. This means the pg.Pool in src/db.js reconnects on each cold start, and long-lived features such as cron jobs and WebSocket connections will not work as expected. For a production deployment that needs persistent state, use Render (documented below) instead of Vercel for the backend.

Backend on Render

The Express server is deployed as a persistent Web Service on Render. Unlike Vercel serverless functions, Render runs a long-lived Node process — which suits Express well for keeping WebSocket connections, cron jobs, and pooled database connections alive.
Cold starts on the free tier: Render’s free Web Service spins down after 15 minutes of inactivity. The first request after a sleep period can take 30–60 seconds to respond while the instance boots. The Yeti Jobs repository includes a GitHub Actions workflow that pings the backend every 5 minutes to prevent sleep. Enable it by ensuring the workflow file is present and the repository’s Actions are enabled.
1

Create a new Web Service

In the Render dashboard click New → Web Service and connect your GitHub repository. Set the root directory to backend.
SettingValue
Root Directorybackend
RuntimeNode
Build Commandnpm ci --omit=dev
Start Commandnode db/migrate.js && node server.js
Instance TypeFree (or paid for always-on)
2

Add all backend environment variables

In the Render dashboard go to your service → Environment and add every backend variable. See the Environment Variables reference for descriptions of each key.
VariableExample value
DATABASE_PASSWORDSupabase connection pooler URL (port 6543)
URL_SUPABASE_CONNECThttps://xyzcompany.supabase.co
ANON_KEY_SUPABASEeyJ...
NODEMAILER_MY_EMAILnoreply@yourdomain.com
NODEMAILER_MY_PASSWORDGmail app password
NODEMAILER_MY_HOSTsmtp.gmail.com
JSON_SECRET_KEYRandom 32+ char string
CLIENT_BASE_URLhttps://yeti-jobs.vercel.app
PORT3000
MAXAGE604800
GROK_APIGroq console API key
3

Deploy

Click Create Web Service. Render builds the container and starts the server. You can watch live logs in the Logs tab. When you see Database connected successfully, the service is ready.Every push to main triggers an automatic redeploy on Render.
Use the Supabase connection pooler, not the direct connection. Render instances use IPv6 by default. The Supabase direct database connection (port 5432) is IPv4-only and will time out from Render. Always use the Session / Transaction pooler URL on port 6543 found in Supabase → Project Settings → Database → Connection pooling. Your DATABASE_PASSWORD variable should look like:
postgresql://postgres.[ref]:[password]@aws-0-us-east-1.pooler.supabase.com:6543/postgres

SSL configuration

src/db.js includes ssl: { rejectUnauthorized: false } in the pg.Pool config so that the Supabase TLS certificate is accepted without requiring a CA bundle on the Render host:
backend/src/db.js
import { Pool } from "pg";
import "dotenv/config";

const client = new Pool({
  connectionString: process.env.DATABASE_PASSWORD,
  ssl: { rejectUnauthorized: false },
  max: 40,
  idleTimeoutMillis: 30000,
  keepAlive: true,
  connectionTimeoutMillis: 10000,
});

export { client as connect };
export { client as Pool };
export default client;
Do not remove ssl: { rejectUnauthorized: false } — without it the TLS handshake to Supabase will fail on Render.

Database and Storage on Supabase

Supabase provides both the PostgreSQL database and the file-storage layer (resumes, profile pictures) for Yeti Jobs.

PostgreSQL setup

1

Create a Supabase project

Go to supabase.com, create a new project, and choose a region closest to your Render service region to minimise latency.
2

Get the connection pooler URL

Navigate to Project Settings → Database → Connection pooling. Copy the Connection string for the Session mode pooler on port 6543. This is the value for DATABASE_PASSWORD.
3

Run migrations

The start command (node db/migrate.js && node server.js) runs all SQL migration files against the database before the server starts. On first deploy this creates all tables, enums, indexes, and triggers defined in the db/ directory.

File storage (Supabase Storage)

Resumes and profile pictures are uploaded directly to Supabase Storage buckets via the @supabase/supabase-js SDK. The backend receives the file through multer, streams it to the appropriate bucket, and saves the returned public URL to the users table (resume_url or profile_pic_url columns). To enable storage:
  1. In your Supabase project go to Storage and create two buckets: one for resumes and one for profile pictures (e.g. resumes, profile-pictures).
  2. Set bucket visibility to Public so URLs returned by the SDK are accessible without authentication tokens.
  3. Ensure URL_SUPABASE_CONNECT and ANON_KEY_SUPABASE are set correctly in your Render environment — the SDK uses them to authenticate storage requests.

Keep-alive for Render free tier

Render’s free tier sleeps instances after 15 minutes of inactivity. Yeti Jobs handles this with a GitHub Actions workflow at .github/workflows/keep-alive.yml that pings the backend health endpoint on a 5-minute schedule:
.github/workflows/keep-alive.yml
name: Keep Render Alive

on:
  schedule:
    - cron: "*/5 * * * *"

jobs:
  ping:
    runs-on: ubuntu-latest
    steps:
      - name: Ping Render
        run: curl https://yeti-jobs.onrender.com/api/v1/health
GitHub Actions free-tier minutes are finite. If you upgrade to a paid Render plan, disable this workflow — a paid Render service never sleeps and the pings are unnecessary.

Build docs developers (and LLMs) love