# CLAUDE.md — `src/pages/api/` Rules for editing `api.js`. This is the **central API layer** — every page calls into it. The root `CLAUDE.md` covers project-wide conventions; this file is the scoped rule sheet for the API layer specifically. --- ## 1. Function signature patterns ### TanStack `useQuery` / `useInfiniteQuery` consumers Destructure from `queryKey` in the order the call site declared it. The leading `_` is the query name and is intentionally discarded. ```js // Plain query — destructure { queryKey }, skip the [0] name slot export const fetchorderscount = async ({ queryKey }) => { const [, appId, startdate, enddate, currentStatus, tenantid, locationid] = queryKey; const url = `${process.env.REACT_APP_URL}/orders/getordersummary/?applocationid=${appId}&...`; const response = await axios.get(url); return response.data.details; }; // Infinite query — also receives pageParam (default to 1) export const fetchOrders = async ({ pageParam = 1, queryKey }) => { const [, appId, currentStatus, debouncedSearch, startdate, enddate, rowsPerPage, tenantid, locationid] = queryKey; const url = `${process.env.REACT_APP_URL}/orders/tenant/getorders/?applocationid=${appId}&...&pageno=${pageParam}&pagesize=${rowsPerPage}`; const response = await axios.get(url); return { rows: response.data.details, nextPage: response.data.details.length === Number(rowsPerPage) ? pageParam + 1 : undefined }; }; ``` **Hard rules:** - The query-key array order at the call site MUST match the destructure order here. Re-ordering one without the other silently breaks every caller. - Infinite queries return `{ rows, nextPage }`. `nextPage` is `undefined` when the page size wasn't filled (signals end-of-stream to `getNextPageParam`). - Some legacy functions return `{ data, nextPage }` instead of `{ rows, nextPage }` (e.g. `getallcustomers`). Match the existing shape rather than "fixing" it — call sites depend on the field name. --- ## 2. Direct positional-argument calls A few functions take plain positional arguments instead of `queryKey` — usually when they're invoked from a `useMutation` or imperatively. Example: `getTenants(appId)`, `gettenantlocations(tenantid)`. Pick the signature based on how the function is called: - Called via `useQuery({ queryFn: fn })` → destructure `{ queryKey }`. - Called via `useQuery({ queryFn: () => fn(arg) })` → take positional args. - Called from a mutation or imperatively → take positional args. --- ## 3. Base URL selection | Use base | When | |---|---| | `process.env.REACT_APP_URL` | Default for ~95% of endpoints | | `process.env.REACT_APP_URL2` | `/users/update`, `/tenants/update`, `/tenants/update/services`, archival `/orders/getorders`, `/partners/getriderlogs` | | Hardcoded `https://routes.workolik.com` | Bike solver + reconcile-steps + batch efficiency | | Hardcoded `https://routemate.workolik.com` | Auto / multi-trip solver | | Hardcoded `https://jupiter.nearle.app` | Login + final `/deliveries/createdeliveries` commit | When adding a new endpoint, check whether the backend actually serves it on URL or URL2 — don't guess. URL2 lives on a separate service. --- ## 4. Error handling pattern ```js try { const response = await axios.get(`${process.env.REACT_APP_URL}/...`); return response.data.details; // or .summary, .data, etc — depends on backend shape } catch (err) { const message = err.response?.data?.message || err.message || 'Something went wrong'; OpenToast(message); return null; // or return [] / {} — match what the caller expects } ``` - Toast on failure via `OpenToast` from `components/third-party/OpenToast`. Don't `throw` — TanStack Query's `onError` is rarely wired by callers. - Return a sensible empty default (`null`, `[]`, `{}`) so the call site's destructuring doesn't crash. - Don't `console.log(err)` AND toast — toast is enough. Some legacy functions do both; new functions should not. --- ## 5. When NOT to add to `api.js` The user has tolerated a number of pages making direct inline `axios.put` / `axios.post` calls for mutations (e.g. `Tenants.js` calls `axios.put('/tenants/update')` inline). Match the surrounding file: - **Adding a new shared GET** → put it in `api.js`. - **Adding a one-off mutation used in only one page** → tolerated inline in that page; doesn't need a new export here. - **Adding a polling endpoint used by multiple pages** → put it here as a named export so the query cache keys are coherent. --- ## 6. Response shape quirks Backend response shapes are inconsistent. Don't assume `response.data.details` — check what the specific endpoint returns: | Backend field | Used by | |---|---| | `response.data.details` | Most list endpoints | | `response.data.summary` | `getcustomersummary`, `gettenantsummary`, `getpricinglist` summary calls | | `response.data.data` | `getRiderPeriodicLogs`, `getallcustomers` infinite-query page payload | | `response.data.message` | Mutation success messages (for toast) | | `response.data.status` | Boolean success flag — check before reading `.details` on some endpoints | When unsure, log the response once during dev and pick the matching field. --- ## 7. Things that look broken but are intentional - `const userid = localStorage.getItem('userid');` at module top — read once at module load, intentional for the `fetchAppLocations` helper. Don't move it inside the function. - Some functions take a `pageno` 0-indexed and others 1-indexed (`pageno: pageParam + 1` vs `pageno: pageParam`). Backend inconsistency — leave it alone unless you confirm the backend side. - A few legacy commented-out function bodies are kept above their current implementation as historical reference. Don't delete them in a drive-by edit.