Skip to main content

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.

FileContents
auth/hook.ts, auth/sessionContext.hook.tsLogin, 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.tsuseRiskDashFeatures — 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 upstream login_2fa; on success sets the encrypted X_VERIFY_ACCESS_2FA verifier cookie. 403s are normalized to code: "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 real NEXT_APP_TOKEN_NAME auth cookie (httpOnly, secure in prod, sameSite=strict). The legacy IS_SUPER_USER/APP_PERMISSION cookies 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 shared getBffAccessToken/bffUnauthorizedJson helpers instead of inline cookie decryption; proxies auth/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/**, and features/index.ts (feature gates).

Proxy & token utilities (lib/functions/)​

FilePurpose
bffAuth.tsThe 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.tsThe 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.tsGeneric encrypted-cookie helpers (encrypt/decrypt/setEncryptedCookie/getDecryptedCookie/removeCookie) built on nookies + crypto-js AES.
redact.tsredactSensitive(value) — deep-redacts any key matching a token/secret/password/cookie pattern before logging; used by commonApiHandler on every failed request.
extractUpstreamErrorMessage.tsNormalizes upstream Django error payloads (string / {message|detail|error} / DRF field-error dict) into one display string.
runCheckAsyncMode.tsresolveRunCheckAsyncMode(req) — forces async (background-worker) mode for security-flag "run check" endpoints unless ALLOW_SYNC_RUN_CHECK=true in local dev.
apiProxyUtil.tsappendUnifiedOrderFilters — shared query-param builder (date range, search, payment status/gateway, broker) reused across order-listing routes.
exposureNormalization.tsConverts upstream 0/1 integer flags into JS booleans for exposure-report data.
storage.lib.tsBrowser-only localStorage/plain-cookie helpers (not part of the BFF chain).
_helpers.lib.tsglobalCatchError/Warning/Sucess — the axios-error → toast notification bridge (via the event emitter), plus permission-denied detection.
util.tsFormatting/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. :::

CookieSet byPurpose
NEXT_APP_TOKEN_NAMElogin-2fa-verify.tsAES-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_USERlogin-2fa-verify.tsLegacy superuser flag. Deprecated for authorization — kept for compatibility only.
NEXT_APP_PERMISSIONlogin-2fa-verify.tsLegacy permissions snapshot. Superseded by live GET /auth/session-context/.
X_VERIFY_ACCESS_2FAlogin_2fa.tsShort-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/*.