← Back to Portfolio
Next.js
React
TypeScript
API Integration
Dashboard

Next.js API Connected Dashboard Services

Client: Logistics SaaS PlatformYear: 2025
Next.js API Connected Dashboard Services

The Challenge

A logistics platform needed a Next.js dashboard consuming five separate third-party REST APIs β€” each with different polling intervals, authentication schemes, and failure modes. Previous implementations created UI jitter, stale data pockets, and a frontend that hammered APIs with redundant concurrent requests. Render cycles peaked at 40+ per second during high-frequency data bursts, causing visible flicker on critical operational panels.

System Architecture Strategy

The architecture separated data transport from rendering. A custom telemetry stream hook centralized all API polling, managed state transitions (loading, live, error, stale), and used a configurable batch window to coalesce rapid updates into single React render cycles. Each data source registered independently with its own polling interval while sharing the same error-boundary and stale-detection logic.

Code Implementation

The custom hook uses a reducer for predictable state transitions and a batch timer ref to collapse concurrent API responses into a single render commit.

// Custom React telemetry stream hook β€” state transitions + batch rendering
import { useReducer, useEffect, useRef, useCallback } from 'react'

type StreamState<T> = {
  data:        T | null
  status:      'idle' | 'loading' | 'live' | 'stale' | 'error'
  lastUpdated: number | null
  error:       string | null
}

type StreamAction<T> =
  | { type: 'FETCH_START' }
  | { type: 'FETCH_SUCCESS'; payload: T; timestamp: number }
  | { type: 'FETCH_ERROR';   error: string }
  | { type: 'SET_STALE' }

function streamReducer<T>(
  state: StreamState<T>,
  action: StreamAction<T>
): StreamState<T> {
  switch (action.type) {
    case 'FETCH_START':
      return { ...state, status: state.data ? 'stale' : 'loading' }
    case 'FETCH_SUCCESS':
      return { data: action.payload, status: 'live',
               lastUpdated: action.timestamp, error: null }
    case 'FETCH_ERROR':
      return { ...state, status: 'error', error: action.error }
    case 'SET_STALE':
      return { ...state, status: 'stale' }
    default:
      return state
  }
}

export function useTelemetryStream<T>(
  fetcher:      () => Promise<T>,
  intervalMs:   number,
  batchWindowMs = 50
) {
  const [state, dispatch] = useReducer(streamReducer<T>, {
    data: null, status: 'idle', lastUpdated: null, error: null,
  })
  const batchTimer    = useRef<ReturnType<typeof setTimeout> | null>(null)
  const pendingUpdate = useRef<T | null>(null)

  const flushBatch = useCallback(() => {
    if (pendingUpdate.current !== null) {
      dispatch({
        type: 'FETCH_SUCCESS',
        payload: pendingUpdate.current,
        timestamp: Date.now(),
      })
      pendingUpdate.current = null
    }
  }, [])

  const fetchAndBatch = useCallback(async () => {
    dispatch({ type: 'FETCH_START' })
    try {
      const result = await fetcher()
      pendingUpdate.current = result
      if (!batchTimer.current) {
        batchTimer.current = setTimeout(() => {
          flushBatch()
          batchTimer.current = null
        }, batchWindowMs)
      }
    } catch (err) {
      dispatch({ type: 'FETCH_ERROR', error: String(err) })
    }
  }, [fetcher, batchWindowMs, flushBatch])

  useEffect(() => {
    fetchAndBatch()
    const id = setInterval(fetchAndBatch, intervalMs)
    return () => {
      clearInterval(id)
      if (batchTimer.current) clearTimeout(batchTimer.current)
    }
  }, [fetchAndBatch, intervalMs])

  return state
}

Scalability Results

Dashboard render cycles dropped from 40+ per second to under 8 on peak data bursts. API request volume reduced by 62% after centralizing polling through the hook. The dashboard now integrates 5 live data sources updating at 2–10 second intervals with no visible lag, and the hook's stale/error states power graceful degradation panels that keep operators informed during upstream outages.