PPactDocs
Developers

Build a custom dashboard with the API + Recharts

Fetch pipeline data from the API and render it as a chart in a React app.

Sometimes you need CRM numbers inside your own app — an exec portal, an internal tool, a wallboard. This recipe pulls deals from the API and renders pipeline-by-stage as a bar chart with Recharts.

Estimated time: ~40 minutes.

1 — Proxy the API from your server

Never call Pact with a pact_live_ key from the browser. Put a thin proxy on your own backend that holds the key and forwards a narrow, read-only slice:

ts
// app/api/pipeline/route.ts  (Next.js route handler)
export async function GET() {
  const res = await fetch("https://app.pact.place/v1/opportunities?limit=500", {
    headers: { Authorization: `Bearer ${process.env.PACT_API_KEY!}` },
    next: { revalidate: 300 }, // cache 5 minutes
  });
  if (!res.ok) return new Response("upstream error", { status: 502 });
  const page = await res.json();
  const byStage: Record<string, number> = {};
  for (const deal of page.items) {
    const stage = deal.stage ?? "unknown";
    byStage[stage] = (byStage[stage] ?? 0) + 1;
  }
  const data = Object.entries(byStage).map(([stage, count]) => ({ stage, count }));
  return Response.json(data);
}

2 — Render with Recharts

tsx
"use client";
import useSWR from "swr";
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts";

const fetcher = (u: string) => fetch(u).then((r) => r.json());

export function PipelineChart() {
  const { data } = useSWR<{ stage: string; count: number }[]>("/api/pipeline", fetcher, {
    refreshInterval: 300_000,
  });
  if (!data) return <p>Loading…</p>;
  return (
    <ResponsiveContainer width="100%" height={320}>
      <BarChart data={data}>
        <XAxis dataKey="stage" />
        <YAxis allowDecimals={false} />
        <Tooltip />
        <Bar dataKey="count" fill="#6366f1" radius={[4, 4, 0, 0]} />
      </BarChart>
    </ResponsiveContainer>
  );
}

Aggregate server-side when you can

This example aggregates a single page of 500 deals in the proxy. For larger pipelines, page through all deals (see nightly backup) or filter by stage so each request stays small and cacheable.

3 — Keep it fresh, not chatty

Cache the proxy response (here, 5 minutes) and let the client poll the proxy, not Pact. One upstream call serves every viewer, so the chart scales to a wall display without burning your rate limit.

What's next?