From Backend Data to Frontend UX

Abhi MuraliAbhi Murali
6 min read

As part of modernizing the SwitchMap-NG web interface, I’ve been focusing on three key areas: improving TypeScript efficiency, developing a dynamic network topology visualization, and consolidating auto-generated documentation into a clean, maintainable frontend site using Docusaurus.

This post highlights practical strategies and tools that make a frontend codebase more robust, scalable, and user-friendly.


Writing TypeScript That Actually Works for You

One of the most common beginner mistakes in TypeScript is using any, especially when dealing with API data. It may silence the compiler, but it also defeats the purpose of using TypeScript—which is meant to catch errors before they become bugs.

Instead of bypassing the compiler, I focused on writing safe, declarative types for all GraphQL responses and internal data structures.

Real Example: Deeply Nested Types for Device Topology

When querying for zone devices via GraphQL, the returned data is deeply nested. I modeled this structure using nested TypeScript types:

export type InterfaceNode = {
  ifoperstatus: number;
  cdpcachedeviceid: string;
  cdpcachedeviceport: string;
};

export type InterfaceEdge = {
  node: InterfaceNode;
};

export type L1Interfaces = {
  edges: InterfaceEdge[];
};

export type DeviceNode = {
  sysDescription: string;
  id: string;
  idxDevice: number;
  sysObjectid: string;
  sysUptime: number;
  sysName: string;
  hostname: string;
  l1interfaces: L1Interfaces;
};

export type DeviceEdge = {
  node: DeviceNode;
};

export type Devices = {
  edges: DeviceEdge[];
};

export type Zone = {
  devices: Devices;
};

export type GetZoneDevicesData = {
  data: {
    zone: Zone;
  };
  errors?: { message: string }[];
};

How It's Used in Practice

When making a GraphQL request to fetch zone device data:

const res = await fetch("/api/graphql", { /* request config */ });
const json: GetZoneDevicesData = await res.json();

Now, json.data.zone.devices.edges is fully typed—I get:

  • Type-safe access to every nested field (e.g., sysName, l1interfaces.edges)

  • Autocomplete suggestions in the IDE

  • Compiler errors if I access something that doesn't exist or gets renamed

Why This Matters

  • Prevents runtime bugs: Mistyped or missing properties are caught at compile time.

  • Improves developer experience: Easier to navigate nested data without constantly checking logs or schemas.

  • Facilitates large-team collaboration: Other contributors can rely on well-defined types to understand API structures.

By creating types that mirror the exact shape of the GraphQL response, I turned what could’ve been fragile, deeply nested object access into predictable and safe code.


Building an Interactive Network Topology with vis-network

The SwitchMap-NG dashboard initially lacked a network topology visualization, even though CDP (Cisco Discovery Protocol) data was already being collected and stored in the backend. To bridge this gap, I developed a dynamic topology graph using vis-network, a powerful vanilla JS visualization library with React integration via refs and hooks.

Fetching CDP Data from the GraphQL API

Using the existing schema, I queried the necessary fields directly from the frontend:

const query = `{
  l1interfaces {
    edges {
      node {
        cdpcachedeviceid
        cdpcachedeviceport
      }
    }
  }
}`;

This query along with a device query provided the relationships needed to visualize device links, the connected device (cdpcachedeviceid), and the port used (cdpcachedeviceport).


Rendering the Graph with vis-network

Instead of using a React wrapper like react-graph-vis, I used vis-network directly via its standalone ES module. This gave me full control over how the chart behaves inside a React component.

Here’s a simplified structure of the component:

import {
  Network,
  DataSet,
  Node,
  Edge,
  Options,
} from "vis-network/standalone/esm/vis-network";

const containerRef = useRef<HTMLDivElement | null>(null);
const networkRef = useRef<Network | null>(null);

I initialized the network using useEffect, populating nodes and edges based on the CDP data. By using DataSet<Node> and DataSet<Edge>, I could update the graph dynamically and manage interactivity (search, reset, highlight) efficiently.


UX Enhancements and Features

  • Hoverable edge labels:
    Labels on edges (like port names) appear only on hover, keeping the graph clean.

  • Custom tooltips on nodes:
    Node title fields contain rich HTML with sysName, hostname, uptime, and sysDescription, rendered using vis-network’s built-in tooltip support.

  • Search & highlight functionality:
    A user can search for a device name, and the graph will zoom in and highlight the matched node while dimming the rest.

  • Reset button:
    Clears the zoom level, restores original node/edge styles, removes selections, and resets the view.

  • Improved usability with clickToUse:
    This mode ensures users can scroll the page naturally and only interact with the graph when they click inside it.


Why vis-network?

While not React-native, vis-network provides fine-grained control over large, dynamic graphs — including physics, performance tuning, and detailed interaction events. By integrating it manually, I avoided extra dependencies while still achieving full React compatibility via useRef.

Limitations:

  • You must manage lifecycle events (like cleanup) manually.

  • TypeScript typing for custom fields (e.g., idxDevice) requires casting or extending interfaces.

  • Limited customizability due to canvas rendering:
    The entire graph is rendered on an HTML <canvas> element, not regular DOM nodes. This makes it harder to insert or style individual elements with CSS or React components — you’re limited to the styling and event hooks provided by the library.

  • Layout tuning (e.g., physics) can require some experimentation.

Despite this, its performance and flexibility made it a solid choice for a real-time network map.


Consolidating and Automating Documentation with Docusaurus

SwitchMap-NG originally had a separate repository for documentation using Docusaurus. To streamline updates and reduce maintenance overhead, I migrated the styles and structural files into the main repo.

Structure & Features

  • Sidebar-based navigation for installation, configuration, code documentation etc.

  • Consistent dark/light modes styling aligned with the SwitchMap branding

  • Links to community resources

The core advantage of Docusaurus is how easy it makes content organization, versioning, and theming—all while being developer-friendly with markdown and React components.

Auto-Generated Docs (Backend + Future Frontend)

The backend (Python) documentation is auto-generated using pydoc, which runs as a pre-commit hook and outputs markdown files into a specific folder (docs/docs/auto-docs).

This ensures:

  • The docs always reflect the current codebase.

  • There’s no manual step to keep docs up to date.

For the frontend (TypeScript), I plan to introduce similar automation using tools like:

  • typedoc — generates markdown or HTML from TypeScript interfaces and comments

  • tsdoc — a standard for comment formatting

Once integrated, the frontend documentation will also update automatically on every commit.


Wrapping Up

These upgrades to SwitchMap-NG focused on making the codebase more reliable, maintainable, and developer-friendly:

  • TypeScript with strong typing reduces bugs and boosts editor support.

  • React-based network topology adds powerful visual insights from backend data.

  • Docusaurus with pre-commit automation keeps documentation clean and consistent.

This approach can be useful for any open-source or internal project aiming to evolve from a prototype into a production-grade system. If you're building something similar, consider investing early in type safety, data visualization, and automated docs—they’ll save you countless hours later.

0
Subscribe to my newsletter

Read articles from Abhi Murali directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Abhi Murali
Abhi Murali

A self-taught software engineer passionate about modern web development. I write about my learning journey, projects, and GSoC experiences using Next.js, React, Tailwind CSS, and GraphQL.