|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Repo layout |
| 6 | + |
| 7 | +Two independent npm projects under one repo. The root `package.json` only wires Husky — there is no root install, no workspaces, and no test runner. Run all commands from inside `client/` or `server/`. |
| 8 | + |
| 9 | +- `client/` — Vite + React 18 + TypeScript SPA |
| 10 | +- `server/` — Express 4 + Mongoose API (ESM, `"type": "module"` in `server/package.json`) |
| 11 | +- `docker-compose.yml` — production stack (`mongo` + `server` + nginx-served `client`) |
| 12 | + |
| 13 | +## Commands |
| 14 | + |
| 15 | +Frontend (`cd client`): |
| 16 | +- `npm run dev` — Vite dev server on `:5173` |
| 17 | +- `npm run build` — `tsc -b && vite build` (the pre-commit hook runs this) |
| 18 | +- `npm run lint` — ESLint over `.ts`/`.tsx` |
| 19 | +- `npm run typecheck` — `tsc --noEmit` |
| 20 | +- `npm run format` — Prettier |
| 21 | + |
| 22 | +Backend (`cd server`): |
| 23 | +- `npm run dev` — nodemon on `src/index.js`, expects `:5000` |
| 24 | +- `npm run start` — production start |
| 25 | +- `npm run format` — Prettier |
| 26 | + |
| 27 | +There is **no test suite anywhere** — quality gates are lint + typecheck + build only. Adding tests is fine; place them as `*.test.tsx` next to the feature or in `__tests__/`. |
| 28 | + |
| 29 | +Docker (production, from repo root): `docker compose up -d --build`. See README "Docker" section for full flow. Local dev does **not** use Docker. |
| 30 | + |
| 31 | +## Architecture — non-obvious things |
| 32 | + |
| 33 | +### Backend API surface is RPC-style, not REST |
| 34 | + |
| 35 | +Routes mount at `/api/user` and `/api/user/report` ([server/src/app.js:41-42](server/src/app.js#L41-L42)) — note the **singular** `user`, despite what the README's tables show. Endpoints are verb-named actions (`/add-today-expenses`, `/get-all-users`, `/received-lent-money`, `/delete-active-session`), not resource paths. When adding endpoints, follow the existing `verifyJwtToken`-then-controller chaining in [server/src/routes/user.routes.js](server/src/routes/user.routes.js) rather than introducing REST conventions. |
| 36 | + |
| 37 | +### Auth middleware is fragile |
| 38 | + |
| 39 | +[server/src/middleware/auth.middleware.js](server/src/middleware/auth.middleware.js) reads the JWT from `Authorization: Bearer <token>` only (no cookie fallback, despite the commented-out code) and **swallows all errors via `console.log`** without calling `next(err)` or returning a response. A bad token currently causes the request to hang. If you touch auth, fix this rather than copying the pattern. |
| 40 | + |
| 41 | +Every authenticated request also writes to `activeSessions.$.lastUsedAt` on the user doc — bulk endpoints will produce one extra Mongo write per call. |
| 42 | + |
| 43 | +### Server is ESM, root is CommonJS |
| 44 | + |
| 45 | +`server/package.json` declares `"type": "module"` so all `.js` files under `server/src` use `import`/`export` with explicit `.js` extensions (e.g. `from './app.js'`). The repo-root `package.json` says `"type": "commonjs"`, but it only holds Husky — don't get confused and switch the server back to require-syntax. |
| 46 | + |
| 47 | +### Frontend state is split three ways |
| 48 | + |
| 49 | +- **Redux Toolkit** ([client/src/app/store.ts](client/src/app/store.ts)) for UI/global client state — slices live in `client/src/features/<domain>/`. |
| 50 | +- **TanStack Query** for all server state (cache, mutations) — wired in [client/src/main.tsx](client/src/main.tsx). |
| 51 | +- **Formik + Yup** for form state and validation; schemas in `client/src/schemas/`. |
| 52 | + |
| 53 | +Don't put server data in Redux. Don't add a second forms library. |
| 54 | + |
| 55 | +### Path alias `@` → `client/src/` |
| 56 | + |
| 57 | +Configured in [client/vite.config.ts:8-9](client/vite.config.ts#L8-L9). Always import internal modules as `@/components/...`, `@/features/...`, etc. — not relative `../../..` paths. |
| 58 | + |
| 59 | +### Vite manualChunks has a known landmine |
| 60 | + |
| 61 | +[client/vite.config.ts:14-31](client/vite.config.ts#L14-L31) splits `pdf` / `framer` / `charts` / `recharts`. The inline comment says **don't** split `react`/`react-dom` into their own chunk — it breaks with "Cannot read properties of undefined (reading 'forwardRef')" because some vendor modules touch React at module-eval time. Leave that alone. |
| 62 | + |
| 63 | +### CORS is an allowlist regex, not a config var |
| 64 | + |
| 65 | +[server/src/app.js:14-24](server/src/app.js#L14-L24) hardcodes `/\.lokeshwardewangan\.in$/` and `/\.vercel\.app$/`. Adding a new origin means editing this file — no env var exists for it. |
| 66 | + |
| 67 | +### Backend response shape |
| 68 | + |
| 69 | +All controllers throw `new ApiError(status, msg)` ([server/src/utils/ApiError.js](server/src/utils/ApiError.js)) for errors and return `new ApiResponse(status, data, msg)` ([server/src/utils/ApiResponse.js](server/src/utils/ApiResponse.js)) for success. Wrap async controllers in `asyncHandler` from [server/src/utils/asyncHandler.js](server/src/utils/asyncHandler.js) to forward rejections to Express. Keep this pattern when adding endpoints. |
| 70 | + |
| 71 | +### Pre-commit hook is heavy |
| 72 | + |
| 73 | +[.husky/pre-commit](.husky/pre-commit) runs `cd client && npm run build && npm run format && git add .` then `cd server && npm run format && git add .` on every commit. A full TypeScript compile + Vite build runs each time — expect ~15–40s per commit. If you need to bypass during a WIP commit, the user must opt in explicitly (no `--no-verify` unless asked). |
| 74 | + |
| 75 | +### Sentry is initialized at module-load |
| 76 | + |
| 77 | +[client/src/App.tsx:13-17](client/src/App.tsx#L13-L17) calls `Sentry.init` unconditionally at import time with `tracesSampleRate: 1.0` and `sendDefaultPii: true`. If `VITE_SENTRY_DSN` is unset, Sentry no-ops — but be aware that 100% trace sampling + PII is on by default in this codebase. |
| 78 | + |
| 79 | +### Docker `VITE_*` are build-time only |
| 80 | + |
| 81 | +In the compose stack ([docker-compose.yml:96-103](docker-compose.yml#L96-L103)), every `VITE_*` variable is passed as a `build.args` entry and inlined into the bundle when the client image builds. Changing them needs `docker compose build client` — a `restart` won't pick them up. The runtime API base for the dockerized client is `/api`, proxied by nginx to `server:5000` (no hardcoded host). |
| 82 | + |
| 83 | +## Conventions |
| 84 | + |
| 85 | +- Naming: PascalCase components, `useXxx` hooks, `xxxSlice` for Redux slices, `domain.routes.js` / `domain.controllers.js` / `domain.model.js` on the server. |
| 86 | +- Frontend is strict TypeScript — avoid `any` (the existing `state: any` in `App.tsx` is a wart, not a pattern to follow). |
| 87 | +- Commit messages use Conventional Commits (`feat:`, `fix:`, `chore:`, `perf:`, `docs:`). Branch names: `feature/...`, `fix/...`, `chore/...`. |
| 88 | +- Prettier config (from CONTRIBUTING.md): `semi: true`, `singleQuote: true`, `trailingComma: "es5"`, `tabWidth: 2`. `prettier-plugin-tailwindcss` reorders class names on save. |
0 commit comments