DUFTHttpClient#

The Bridge Between DUFT UI and DUFT Server#

At the heart of DUFT’s architecture is the DUFT Server, responsible for executing queries, running ETL processes, handling authentication, and serving configuration-driven dashboards. But for DUFT UI to function seamlessly, it needs a way to interact with the backend—fetching data, running tasks, and staying in sync with server-side updates. That’s where DuftHttpClient comes in.

This class serves as the communication bridge between DUFT UI and DUFT Server’s API, abstracting away the complexity of making HTTP requests. Instead of hardcoding API calls throughout the frontend, DuftHttpClient centralises everything in one place, ensuring that queries, authentication, and configuration retrieval are handled efficiently and securely.

Managing Authentication and Token Refresh#

One of the most critical aspects of DuftHttpClient is how it handles authentication. DUFT Server requires secure access, meaning API calls must be authenticated using Bearer tokens. The client stores and retrieves these tokens as needed, ensuring that every request includes proper credentials.

But authentication tokens expire, and that’s where automatic token refreshing comes into play. If a request is denied due to an expired token, DuftHttpClient intercepts the failure, retrieves a fresh token from the server, and retries the request seamlessly—all without requiring the user to log in again. This prevents unnecessary session interruptions and keeps the UI responsive, even when tokens expire in the background.

Running Queries and Fetching Data#

DUFT’s core functionality revolves around querying data sources. Whether it’s pulling records from a database, retrieving structured analytics, or executing server-side transformations, DuftHttpClient is responsible for sending the request, waiting for execution, and returning the results in a structured format.

Queries are executed dynamically, either as stored SQL queries or inline queries defined in DUFT Config. The client takes care of passing user-supplied parameters, handling pagination, and ensuring that queries execute against the correct database connection. Once the results are returned, they’re transformed into structured JSON, ready to be displayed in DUFT UI.

Beyond SQL queries, DuftHttpClient also interacts with data tasks—custom Python scripts that perform Extract, Transform, Load (ETL) operations. These tasks may fetch data from external APIs, clean and aggregate information, or push processed results back into a database. The client ensures that tasks execute asynchronously, reporting progress back to the UI in real time.

Keeping the UI in Sync with DUFT Config#

DUFT’s configuration-driven architecture means that UI components don’t store their own structure—they retrieve their layout, filters, and data sources dynamically from DUFT Config. DuftHttpClient plays a crucial role in this, fetching the latest dashboard definitions, navigation menus, and theming settings from DUFT Server.

By decoupling the UI from hardcoded components, new dashboards can be introduced without redeploying the frontend. The client ensures that as soon as an update is made to DUFT Config—whether that’s adding a new data source, adjusting filters, or changing how queries run—the UI reflects those changes immediately.

A Seamless User Experience#

DuftHttpClient is more than just an API wrapper. It’s designed to handle failures gracefully, retrying requests when needed, managing authentication behind the scenes, and providing real-time feedback for data tasks. It abstracts away the complexities of HTTP requests, ensuring that DUFT UI developers can focus on building dashboards instead of worrying about API intricacies.

Ultimately, this class is what makes DUFT’s declarative, configuration-driven UI possible. By dynamically fetching dashboards, queries, and UI elements at runtime, it enables a system that is fully adaptable, extensible, and entirely driven by configuration, without requiring frontend developers to manually wire up every component.

DuftHttpClient Usage examples#

Here are some usage examples of the client.

Initialising the Client#

To start using DuftHttpClient, you need to create an instance and optionally provide token storage functions.

// Define storage for authentication tokens (optional)

let tokenStore: string | null = null;
const getTokenFromStore = (): string | null => tokenStore;
const setTokenInStore = (token: string): void => {
  tokenStore = token;
};

// Create an instance of DuftHttpClient
const client = new DuftHttpClient(
  "http://127.0.0.1:8000/api/v2", 
  getTokenFromStore, 
  setTokenInStore
);

Logging in and Storing Authentication Tokens#

Before accessing protected routes, the user must authenticate.

const username = "data_manager";
const password = "securepassword";

async function loginUser() {
  try {
    const response = await client.login(username, password);
    console.log("Login successful! Token:", response.access);
  } catch (error) {
    console.error("Login failed:", error);
  }
}

loginUser();

Once logged in, the authentication token is stored and used for subsequent API calls.

Fetching the Current System Configuration#

DUFT relies on external configuration files. DuftHttpClient fetches the latest config dynamically.

async function fetchConfig() {
  try {
    const config = await client.getCurrentConfig();
    console.log("Current Configuration:", config);
  } catch (error) {
    console.error("Failed to fetch configuration:", error);
  }
}

fetchConfig();

Fetching the Navigation Menu#

DUFT UI dynamically generates menus based on the navigation.json file stored in DUFT Config.

async function loadNavigation() {
  try {
    const navigation = await client.getNavigationFile();
    console.log("Navigation Data:", navigation);
  } catch (error) {
    console.error("Error loading navigation:", error);
  }
}

loadNavigation();

Running a Query to Fetch Data#

Queries power dashboards in DUFT. The client sends queries to DUFT Server and retrieves structured JSON responses.

const queryPayload = {
  query: "SELECT COUNT(*) FROM fact_hiv_status WHERE is_suppressed = 1",
  data_connection_id: "ANA",
};

async function runQuery() {
  try {
    const data = await client.getQueryData(queryPayload);
    console.log("Query Result:", data);
  } catch (error) {
    console.error("Error executing query:", error);
  }
}

runQuery();

Running a Data Task#

DUFT allows executing ETL jobs (Extract, Transform, Load) via Data Tasks. The client can trigger them and monitor progress.

const taskPayload = {
  taskId: "update_hiv_data",
  parameters: { reporting_month: "2024-01" },
};

async function executeDataTask() {
  try {
    const taskResult = await client.runDataTask(taskPayload);
    console.log("Data Task Executed:", taskResult);
  } catch (error) {
    console.error("Error running data task:", error);
  }
}

executeDataTask();

Fetching and Updating Data Connection Parameters#

DUFT supports multiple databases, and their credentials/configurations can be retrieved or updated dynamically.

async function updateDatabaseConnection() {
  try {
    const connectionId = "ANA"; // Example connection ID
    const newParams = {
      server: "db.example.com",
      username: "new_user",
      password: "new_secure_password",
    };

    const result = await client.updateConnectionParameters(connectionId, newParams);
    console.log("Database Connection Updated:", result);
  } catch (error) {
    console.error("Error updating database connection:", error);
  }
}

updateDatabaseConnection();