Skip to content

Backend Guide (API-honeyhimself)

Stack: Express.js + TypeScript, MongoDB/Mongoose, Firebase Admin SDK, Zod, Socket.io


Directory Structure

DirectoryPurpose
src/controllers/Request handlers — one class per resource
src/models/Mongoose schemas — one file per collection
src/routes/Express routers — mounted in routes/v1/index.ts
src/services/Business logic and integrations (Email, AI, question dedup)
src/middleware/Auth, error handling, validation
src/scripts/Seed scripts — run with npx tsx src/scripts/*.ts
src/utils/Shared utilities (candidateProjection.ts, etc.)

Running Locally

bash
cd API-honeyhimself
npm install
npm run dev

Requires .env with: MONGO_URI, FIREBASE_SERVICE_ACCOUNT, ANTHROPIC_API_KEY, RESEND_API_KEY, FRONTEND_URL, API_URL


Coding Conventions

  • Controllers are classes with async methods
  • All handlers: try/catch → next(error)
  • Mongoose patterns: lean() on list queries, populate() on single-doc queries
  • Validation: Zod schemas defined at module scope (not inside class), validated before business logic → 400 on failure
  • Auth: authMiddleware applied at router level (router.use(authMiddleware)); req.user and req.organizationId available in all protected routes

Auth Middleware

Two auth middlewares, one per actor type:

authMiddleware.ts (Recruiters)

typescript
// Applied at router level: router.use(authMiddleware)
// 1. Verify Firebase idToken → uid
// 2. User.findOne({ authId: uid }) → req.user
// 3. Organization.findOne({ 'members.userId': req.user._id }) → req.organizationId

candidateAuthMiddleware.ts (Candidates — Phase 2)

typescript
// 1. Verify Firebase idToken → { uid, email }
// 2. Participant.findOne({ authId: uid }) → req.participant
// 3. If not found → onCandidateFirstLogin(uid, email)

Route Mount Points

All routes mounted under /v1/ in src/routes/v1/index.ts:

/v1/auth/*                  — recruiter auth (Firebase)
/v1/users/*                 — user profile
/v1/jobs/*                  — job openings (CRUD)
/v1/interviews/*            — interview scheduling + feedback
/v1/stage-types             — GET only — returns all active StageTypeConfigs
/v1/screening/*             — public: token-gated screening sessions
/v1/dsa/*                   — public: token-gated DSA sessions (Phase 3)
/v1/ai-session/*            — public: token-gated AI sessions (Phase 4)
/v1/pipeline/*              — candidate pipeline management
/v1/candidate/*             — candidate dashboard (candidateAuthMiddleware)
/v1/screening-questions/*   — screening question bank
/v1/interview-questions/*   — interview question bank (Phase 1B)
/v1/dsa-problems/*          — DSA problem bank (Phase 3)
/v1/scenario-questions/*    — scenario question bank (Phase 4)
/v1/orgs/*                  — organization management (Phase 5)
/v1/plans                   — public: list active plans
/v1/analytics/*             — analytics (Phase 6, gated by advancedAnalytics)
/v1/webhooks/stripe         — Stripe webhooks (Phase 5)
/health                     — health check

Seed Scripts

Run via npm run seed:* (scripts in package.json):

ScriptPurpose
npm run seed:plansInsert 4 Plan docs (free/starter/pro/enterprise)
npm run seed:stage-typesInsert 6 StageTypeConfig docs
npm run seed:screening-questionsInsert 22 system ScreeningQuestion docs
npm run seed:dev-orgCreate org for dev user honey.singhroi@gmail.com (requires seed:plans first)
npm run seed:allRuns plans → stageTypes → screeningQuestions → devOrg (via seedAll.ts master script)
npm run seed:interview-questionsInsert ~30 system InterviewQuestion docs — Phase 1B, NOT in seed:all
npm run seed:dsa-problemsInsert 20+ system DSAProblem docs — Phase 3, NOT in seed:all
npm run seed:scenario-questionsInsert system ScenarioQuestion docs — Phase 4, NOT in seed:all

Scripts use npx tsx (not ts-node).


Error Handling

All route handlers follow the try/catch → next(error) pattern. A global error handler middleware formats errors and responds:

  • 400 — Zod validation failure
  • 401 — Missing or invalid Firebase token
  • 403 — Usage limit exceeded or insufficient plan
  • 404 — Resource not found
  • 409 — Conflict (duplicate active interview for same stage)
  • 500 — Unexpected server error (logged with request ID)