How the Express backend is organized, how configuration is validated at startup, and which protections run by default — verified against the actual files.
server.js first loads .env (dotenv), builds the app from app.js, connects MongoDB via config/db.js, and then listens. The API mounts under API_PREFIX (default /api/v1) from config; the health endpoint lives at /api/v1/health.
config/index.js validates process.env with a Joi schema the moment the app boots. Required variables: MONGO_URI, JWT_SECRET (32+ chars), JWT_REFRESH_SECRET (32+), COOKIE_SECRET (32+), FRONTEND_URL. RESEND_API_KEY and RESEND_FROM are optional at boot so local development still works without email credentials, but production delivery requires both. Everything else has defaults (PORT=5000, API_PREFIX=/api/v1, NODE_ENV=development, JWT_EXPIRES_IN=7d…). With any invalid value the server exits immediately and prints “Config validation error: …” naming every bad variable — this is intentional fail-fast behaviour, not a bug.
Two levels: the global IP limiter configured by env, plus the email-specific pair (3 forgot-OTP and 5 verify/reset attempts per email per 15 min) keyed by IP+email in authRoutes.js — those two read their numbers directly from process.env (RATE_LIMIT_FORGOT_*, RATE_LIMIT_OTP_*) instead of the Joi schema.
src/utils/logger.js wraps pino with pino-pretty. All errors print structured details; in development, when RESEND_API_KEY is missing or delivery fails, the email service falls back to logging emails to the console and the OTP reset flow additionally surfaces the code in the API response. In production none of that happens.
pnpm test # Jest suite (272 tests) with coverage
test:watch # re-run on file changes
pnpm lint # ESLint (eslint 9 flat config)
pnpm lint:fix
pnpm format # Prettier
Tests run against mongodb-memory-server, so they never touch your real database.