Backend Guide (API-honeyhimself)
Stack: Express.js + TypeScript, MongoDB/Mongoose, Firebase Admin SDK, Zod, Socket.io
Directory Structure
| Directory | Purpose |
|---|---|
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 devRequires .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:
authMiddlewareapplied at router level (router.use(authMiddleware));req.userandreq.organizationIdavailable 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.organizationIdcandidateAuthMiddleware.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 checkSeed Scripts
Run via npm run seed:* (scripts in package.json):
| Script | Purpose |
|---|---|
npm run seed:plans | Insert 4 Plan docs (free/starter/pro/enterprise) |
npm run seed:stage-types | Insert 6 StageTypeConfig docs |
npm run seed:screening-questions | Insert 22 system ScreeningQuestion docs |
npm run seed:dev-org | Create org for dev user honey.singhroi@gmail.com (requires seed:plans first) |
npm run seed:all | Runs plans → stageTypes → screeningQuestions → devOrg (via seedAll.ts master script) |
npm run seed:interview-questions | Insert ~30 system InterviewQuestion docs — Phase 1B, NOT in seed:all |
npm run seed:dsa-problems | Insert 20+ system DSAProblem docs — Phase 3, NOT in seed:all |
npm run seed:scenario-questions | Insert 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 failure401— Missing or invalid Firebase token403— Usage limit exceeded or insufficient plan404— Resource not found409— Conflict (duplicate active interview for same stage)500— Unexpected server error (logged with request ID)