Skip to main content

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 VariableTarget API / ServicePurpose / Core Features
NEXT_PUBLIC_USER_API_BASE_URL
NEXT_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_URL
NEXT_PUBLIC_PRODUCT_V2_API_BASE_URL
NEXT_PUBLIC_PRODUCT_V3_API_BASE_URL
Safety Analytical Plus APICore safety analytics, Periodic Safety Update Reports (PSUR), IMDRF definitions, and regulatory complaints.
NEXT_PUBLIC_PRODUCT_API_DR_API_BASE_URL
NEXT_PUBLIC_PRODUCT_SNOWFLAKE
DataReality Core / Snowflake APIRisk IQ session files, data imports (complaints, hazards, nonconformances), recall metrics, and 510(k) devices.
NEXT_PUBLIC_AI_API_BASE_URLAI & ML Engine APIAI classifications, incident-to-hazard mapping, failure mode/cause predictions, and automated executive summaries.
NEXT_PUBLIC_ALERT_URLAlert Service APIFetching, creating, dismissing, and archiving dashboard regulatory/system alerts.
NEXT_PUBLIC_OPENALEX_API_KEYOpenAlex 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

  1. Auth Guard: For non-auth endpoints (e.g., excluding /signin, /registration, /forgot, /reset), if there is no userId in local storage and it's not a public page, the interceptor clears local storage and redirects the user to /login.
  2. Authorization Header: Automatically appends the active authToken retrieved from storage.
  3. Feature Entitlements: Checks the current window pathname to attach:
    • x-tdr-feature: Derived via getEntitlementNameByPath(pathname) to restrict access to specific features.
    • x-tdr-role: Derived via getRoleByPath(pathname) to pass the active role context.
  4. Tenant/User Context: Attaches x-tdr-userid and x-tdr-companyid headers 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

🏢 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 as multipart/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-ID and API-KEY alongside 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
  • 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:

  1. Check Base URLs: Make sure the target base URL is defined in .env and declared at the top of your API file.
  2. Define TypeScript Types: Add appropriate request payloads and response interface models inside src/Interfaces or the API file.
  3. Write the API Function:
    • Import axiosInstance from src/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;
    };
  4. 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.