5.7 KiB
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.
// 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 }.nextPageisundefinedwhen the page size wasn't filled (signals end-of-stream togetNextPageParam). - 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
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
OpenToastfromcomponents/third-party/OpenToast. Don'tthrow— TanStack Query'sonErroris 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 thefetchAppLocationshelper. Don't move it inside the function.- Some functions take a
pageno0-indexed and others 1-indexed (pageno: pageParam + 1vspageno: 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.