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:
// 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
"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.