API & BFF layer
api/ · lib/functions/ · pages/api/ · routes/apiPaths.ts · proxy.ts
The frontend never talks to the upstream Django "Risk API" directly. Every request goes: React component → React Query hook → shared Axios instance → a Next.js API route (the BFF) → commonApiHandler → upstream, with the bearer token resolved server-side from an encrypted cookie on every hop.
Two parallel endpoint tables exist by design: api/endpoints/index.ts (relative paths the browser calls, always its own /api/...) and routes/apiPaths.ts (absolute upstream URLs the API routes call). Every dashboard/report feature has a matching entry in both, plus one file under pages/api/dashboard/**.
Axios client
api/axiosInstance/index.ts — single shared Axios instance, baseURL: "/api/". Augments requests with a skipGlobalErrorToast flag so callers (e.g. the login form) can own their own error UI. The response interceptor fires success toasts for URLs listed in sucessNotificationEndPoints, and on a 401 shows a one-shot "session expired" toast and reloads the page; other errors route through globalCatchError unless suppressed.
api/endpoints/index.ts exports the endpoints.{auth,dashboard,reports} path tree consumed as the first argument to every axiosInstance.get/post/... call, plus baseUrl/mediaUrl() helpers.
React Query hooks (api/hooks/)
All data-fetching hooks live here, one file per feature domain, all following the same shape — useQuery/useMutation against an endpoints.* path, with mutations invalidating related query keys on success. ~280 exported hooks total.
| File | Contents |
|---|---|
auth/hook.ts, auth/sessionContext.hook.ts | Login, 2FA verify/resend, logout; useSessionContextQuery — the live RBAC poll (45s stale, 90s interval), documented in Auth & session |
dashboard/hook.ts (~3,000 lines, ~100 hooks) | The bulk of KPI/summary queries, trader detail/performance, customer/order data, payout operations (stage counts, transitions, bulk actions, Rise reinvite/retry, risk summaries), exposure/hedging reports, TOS-breach / idle-time run-check flows |
dashboard/securityFlags.hook.ts (56 hooks) | Security-flag list/detail, review & verification workflow, correction tasks, user-action submissions, enforcement (single/custom/bulk), notices, platform-ban preview/confirm/reverse, IP/account/user history |
dashboard/securityFlagsExceptions.hook.ts (46 hooks) | Exception-rule governance: readiness/runtime status, intelligence dashboard, review queue, rule CRUD, approval workflow, recertifications, bulk ownership/extend/activate |
dashboard/securityFlagsPolicyCenter.hook.ts (24 hooks) | Policy-version lifecycle: overview/versions/matrix/compare, simulator, clone, validate, activation impact/activate, review workflow, rollback preview/clone, audit history, emergency pause |
dashboard/complianceCases.hook.ts (13 hooks) | Case list/detail, operations dashboard, teams/decision types, create/assign, notes/communication, decision, close/reopen |
dashboard/riskDashFeatures.hook.ts | useRiskDashFeatures — feature-flag snapshot (compliance_manual_flags, compliance_cases, flag_exceptions, payout_operations, …); treats 403/404 as "all off" rather than an error so gated UI degrades gracefully |
*.schema.ts files (paired with each hook file) | Request/response TypeScript types — dashboard/schema.ts alone is ~1,900 lines; securityFlags.schema.ts ~2,450 lines |
Next.js API routes / BFF proxy (pages/api/)
pages/api/auth/** (5 routes) and pages/api/dashboard/** (≈250 routes, one file per upstream endpoint) are thin proxy handlers. Almost all follow the same template: decrypt the Knox token from the auth cookie → build the upstream URL from API_ENDPOINTS.* → call commonApiHandler → normalize the result to { success, data } or { success: false, message }.
pages/api/auth/login_2fa.ts— POST, no auth required. Validates credentials, calls upstreamlogin_2fa; on success sets the encryptedX_VERIFY_ACCESS_2FAverifier cookie. 403s are normalized tocode: "risk_dashboard_access_denied".pages/api/auth/login-2fa-verify.ts— POST. Verifies the OTP against the verifier cookie; on success clears bootstrap cookies and sets the realNEXT_APP_TOKEN_NAMEauth cookie (httpOnly, secure in prod,sameSite=strict). The legacyIS_SUPER_USER/APP_PERMISSIONcookies are explicitly marked deprecated — live session-context is authoritative post-login.pages/api/auth/session-context.ts— GET. The one route already migrated to the sharedgetBffAccessToken/bffUnauthorizedJsonhelpers instead of inline cookie decryption; proxiesauth/session-context/, the source of truth for RBAC.pages/api/dashboard/**— ≈250 one-file-per-endpoint proxies grouped by feature: core KPIs, trader/customer/order detail, payout operations (incl. dynamic[id]transition/preview/notes/kyc + bulk-* routes), exposure/hedging,security-flags/**(the largest subtree),compliance-cases/**,idle-time/**,tos-breach/**, andfeatures/index.ts(feature gates).
Proxy & token utilities (lib/functions/)
| File | Purpose |
|---|---|
bffAuth.ts | The canonical token resolver: getBffAccessToken(req) decrypts the Knox token from the auth cookie and validates it looks plausible (catches silent AES-decrypt failures on a wrong key), returning {token} or a typed error. Fails closed if the secret is missing. |
commonApiHandler.ts | The single upstream HTTP client used by every route: method allowlisting, token injection, fetch() with an AbortController timeout (default 60s), upstream-timeout vs. client-disconnect distinction (504 vs 499), 401 → destroys auth cookies, binary/JSON/CSV response handling, redacted error logging. |
cookieHelper.ts | Generic encrypted-cookie helpers (encrypt/decrypt/setEncryptedCookie/getDecryptedCookie/removeCookie) built on nookies + crypto-js AES. |
redact.ts | redactSensitive(value) — deep-redacts any key matching a token/secret/password/cookie pattern before logging; used by commonApiHandler on every failed request. |
extractUpstreamErrorMessage.ts | Normalizes upstream Django error payloads (string / {message|detail|error} / DRF field-error dict) into one display string. |
runCheckAsyncMode.ts | resolveRunCheckAsyncMode(req) — forces async (background-worker) mode for security-flag "run check" endpoints unless ALLOW_SYNC_RUN_CHECK=true in local dev. |
apiProxyUtil.ts | appendUnifiedOrderFilters — shared query-param builder (date range, search, payment status/gateway, broker) reused across order-listing routes. |
exposureNormalization.ts | Converts upstream 0/1 integer flags into JS booleans for exposure-report data. |
storage.lib.ts | Browser-only localStorage/plain-cookie helpers (not part of the BFF chain). |
_helpers.lib.ts | globalCatchError/Warning/Sucess — the axios-error → toast notification bridge (via the event emitter), plus permission-denied detection. |
util.ts | Formatting/crypto helpers: formatCurrency, maskEmail/maskPhoneNumber, toEST, and a second independent encryptData/decryptData AES pair for arbitrary client payloads. |
:::warning Known inconsistency
bffAuth.getBffAccessToken is the intended shared token-resolution helper, but 299 of ~300 route files under pages/api/** still decrypt the cookie inline and pass an explicit Authorization header instead of using it — only session-context.ts has been migrated. Worth knowing before touching auth logic in any given route.
:::
Auth cookie map
| Cookie | Set by | Purpose |
|---|---|---|
NEXT_APP_TOKEN_NAME | login-2fa-verify.ts | AES-encrypted Knox bearer token; httpOnly, secure in prod, sameSite=strict. The only cookie that actually gates API calls — forwarded upstream as Authorization: Fff <token>. |
NEXT_APP_SUPER_USER | login-2fa-verify.ts | Legacy superuser flag. Deprecated for authorization — kept for compatibility only. |
NEXT_APP_PERMISSION | login-2fa-verify.ts | Legacy permissions snapshot. Superseded by live GET /auth/session-context/. |
X_VERIFY_ACCESS_2FA | login_2fa.ts | Short-lived verifier token bridging first-factor login to OTP verification; cleared after success. |
All auth cookies are destroyed together on logout, on any upstream 401, or on a detected decrypt failure. The real authorization source of truth at runtime is the live capabilities map from GET /auth/session-context/ — see Auth & session.
proxy.ts (repo root, Next.js Middleware) — page-level route protection, separate from API-layer auth: checks raw presence of the token cookie (not decrypted) to redirect unauthenticated users away from /dashboard/* and authenticated users away from /auth/*.