API Architecture & Integration Guide
Welcome to the team! This guide provides a detailed overview of the API architecture inside the DataReality UI codebase. It covers the services we interact with, how the HTTP client layer is structured, how authentication and context are managed, and how APIs are consumed in components.
1. Core API Gateways (Base URLs)
DataReality connects to multiple microservices and external data providers. These endpoints are configured via environment variables in the .env files:
| Environment Variable | Target API / Service | Purpose / Core Features |
|---|---|---|
NEXT_PUBLIC_USER_API_BASE_URLNEXT_PUBLIC_USER_V2_API_BASE_URL | User & Identity API (v1/v2) | Auth (sign-in, registration, resets), company profile management, user management, and roles/entitlements. |
NEXT_PUBLIC_PRODUCT_V1_API_BASE_URLNEXT_PUBLIC_PRODUCT_V2_API_BASE_URLNEXT_PUBLIC_PRODUCT_V3_API_BASE_URL | Safety Analytical Plus API | Core safety analytics, Periodic Safety Update Reports (PSUR), IMDRF definitions, and regulatory complaints. |
NEXT_PUBLIC_PRODUCT_API_DR_API_BASE_URLNEXT_PUBLIC_PRODUCT_SNOWFLAKE | DataReality Core / Snowflake API | Risk IQ session files, data imports (complaints, hazards, nonconformances), recall metrics, and 510(k) devices. |
NEXT_PUBLIC_AI_API_BASE_URL | AI & ML Engine API | AI classifications, incident-to-hazard mapping, failure mode/cause predictions, and automated executive summaries. |
NEXT_PUBLIC_ALERT_URL | Alert Service API | Fetching, creating, dismissing, and archiving dashboard regulatory/system alerts. |
NEXT_PUBLIC_OPENALEX_API_KEY | OpenAlex Scholarly Database (External) | Literature database queries, semantic searches (similar works), authors, and publication citations. |
2. HTTP Client Layer: Axios Singleton
To keep configurations consistent, we do not use the raw axios import directly for internal APIs. Instead, we use a custom configured Axios singleton defined in src/service/axios.tsx.
This singleton sets default headers and uses a Request Interceptor to inject session details, security contexts, and feature entitlements.
Interceptor Functionality
- Auth Guard: For non-auth endpoints (e.g., excluding
/signin,/registration,/forgot,/reset), if there is nouserIdin local storage and it's not a public page, the interceptor clears local storage and redirects the user to/login. - Authorization Header: Automatically appends the active
authTokenretrieved from storage. - Feature Entitlements: Checks the current window pathname to attach:
x-tdr-feature: Derived viagetEntitlementNameByPath(pathname)to restrict access to specific features.x-tdr-role: Derived viagetRoleByPath(pathname)to pass the active role context.
- Tenant/User Context: Attaches
x-tdr-useridandx-tdr-companyidheaders to identify the caller and scope data securely.
Source Reference: Axios Setup
// Located in src/service/axios.tsx
export class Axios {
private static instance: AxiosInstance;
static getInstance(): AxiosInstance {
if (this.instance) return this.instance;
this.instance = axios.create({
headers: {
mode: "no-cors",
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
this.instance.interceptors.request.use(
(config) => {
const userToken = LocalStorage.getItem("authToken");
const userId = LocalStorage.getItem("userId");
const companyId = LocalStorage.getItem("companyId");
// Auth Redirect Check
if (!isAuthRequest(config.url) && !userId && !isOnPublicPage()) {
LocalStorage.clear();
window.location.href = "/login";
return Promise.reject(new Error("Redirecting to login."));
}
const headers = config.headers ?? {};
headers.Authorization = userToken || "";
// Attach Feature Entitlements based on current URL path
if (typeof window !== "undefined") {
const { pathname } = window.location;
const entitlementName = getEntitlementNameByPath(pathname);
const roleName = getRoleByPath(pathname);
if (entitlementName) headers["x-tdr-feature"] = entitlementName;
if (roleName) headers["x-tdr-role"] = roleName;
}
// Attach Context
if (userId) headers["x-tdr-userid"] = userId;
if (companyId) headers["x-tdr-companyid"] = companyId;
config.headers = headers;
return config;
},
(error) => Promise.reject(error)
);
return this.instance;
}
}
export const axiosInstance = Axios.getInstance();
3. State Management & React Query Integration
For client-side caching, loading indicators, and server-state updates, we wrap our API service functions with TanStack React Query (v4).
Custom query and mutation hooks are organized in the src/Hooks/ folder.
Pattern Example: Custom Query Hook
// Located in src/Hooks/useUserGroups.ts
import { useQuery } from "@tanstack/react-query";
import { useLocalStorage } from "@mantine/hooks";
import { getUserGroups } from "src/api/group/getUserGroup";
export const useUserGroups = () => {
const [userId] = useLocalStorage({ key: "userId", defaultValue: "" });
const { data, isLoading, error } = useQuery(
["userGroups", userId], // Query Key
() => getUserGroups({ userId }), // Fetcher Function
{ enabled: !!userId } // Guard Configuration
);
const userGroups = data?.map((group) => ({
id: group.id,
name: group.name,
})) || [];
return { userGroups, isLoading, error };
};
4. Key API Modules & Directories
All API requests are modularized and contained within src/api/. Here is what each folder does:
🔐 Authentication & Identity
- Files:
login.tsx,users.tsx,changePassword.ts - Endpoints Used:
/users/signin,/users/registration,/users/forgot,/users/reset - Base URL:
NEXT_PUBLIC_USER_API_BASE_URL
🏢 Companies & Profiles
- Files:
company.tsx,profile.tsx - Endpoints Used:
/companies,/companies/{id},/companies/{id}/users - Base URL:
NEXT_PUBLIC_USER_API_BASE_URL
⚙️ PSUR & Regulatory Reporting
- Files:
psur.ts,psurAdminConfig.ts,terumoPsur.tsx - Endpoints Used:
/psur,/psur/fileNames,/annex/{annexType},/recalls - Base URL:
NEXT_PUBLIC_PRODUCT_V1_API_BASE_URL&NEXT_PUBLIC_PRODUCT_V2_API_BASE_URL
📊 IntelliRisk IQ (Risk Management)
- Files:
riskIq.ts,fmea.tsx,hazard.tsx - Endpoints Used:
/riskiq/files(Retrieve/create risk sessions)/riskiq/import/both(Upload Excel complaint/hazard lists asmultipart/form-data)/riskiq/export/{id}(Download computed session results)
- Base URL:
NEXT_PUBLIC_PRODUCT_SNOWFLAKE(Snowflake core data API)
🤖 AI Engine Integrations
- Directories:
src/api/ai/ - Headers Required: Sends
CLIENT-IDandAPI-KEYalongside payloads. - Core Endpoints:
/failure_cause(Analyze causes)/failure_mode(Analyze modes)/hazard&/hazard_situation(Predict risk attributes)/imdrf_code(Match complaints to regulatory codes)/riskiq/complaintclassification&/riskiq/complainthazardmapping(Evaluate uploads)/executivesummary/introsummary(Compile executive commentary for sections)
- Base URL:
NEXT_PUBLIC_AI_API_BASE_URL
📚 Literature & Bibliography Search
- Files:
OpenAlexService.ts - Core Endpoints:
/works(Query books/journals)/autocomplete/{entityType}(Author, institution, topic searches)/find/works(AI embedding semantic match)
- Details: Implements local backoff retry logic (exponential backoff up to 5 attempts) to prevent rate limits (HTTP 429).
- Base URL:
https://api.openalex.org
5. Quick Cheat Sheet: Adding a New API Call
When tasked with integrating a new API endpoint, follow these four structured steps:
- Check Base URLs: Make sure the target base URL is defined in
.envand declared at the top of your API file. - Define TypeScript Types: Add appropriate request payloads and response interface models inside
src/Interfacesor the API file. - Write the API Function:
- Import
axiosInstancefromsrc/service/axios. - Call the endpoint using standard HTTP verbs (
get,post,put,delete).
export const createCustomReport = async (payload: CustomReportPayload) => {
const response = await axiosInstance.post<CustomReportRes>(
`${process.env.NEXT_PUBLIC_PRODUCT_V1_API_BASE_URL}/custom-report`,
payload
);
return response.data;
}; - Import
- Wrap with React Query Hook:
- Create a hook in
src/Hooks/to manage this API call. This handles local caching, refetches, and keeps standard state sync clean.
- Create a hook in