A static site can contain live GraphJSON charts without making a secret-bearing API call in the browser. Generate iframe URLs during the build and render the resulting URLs into static HTML.
When this pattern fits#
Use build-time generation when:
- the chart configuration is the same for every visitor
- relative ranges such as
30 days agoshould advance when the iframe loads - a rebuild is acceptable when you change filters or styling
- the generated chart is appropriate for everyone who can access the page
For per-user authorization, use personalized dashboards instead.
Create a server-only helper#
// lib/graphjson.js
export async function createRevenueChart() {
const response = await fetch(
"https://api.graphjson.com/api/visualize/iframe",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
api_key: process.env.GRAPHJSON_API_KEY,
collection: "billing_events",
IANA_time_zone: "UTC",
graph_type: "Single Line",
start: "30 days ago",
end: "now",
filters: [
["event", "=", "payment_succeeded"],
["currency", "=", "usd"]
],
metric: "amount",
aggregation: "Sum",
compare: "30 days ago",
granularity: "Day",
customizations: {
title: "Daily revenue"
}
})
}
);
const body = await response.json();
if (!response.ok) {
throw new Error(body.error || `GraphJSON returned ${response.status}`);
}
return body.url;
}
Keep this module out of client bundles. Its call requires GRAPHJSON_API_KEY.
Generate with Next.js#
For a Pages Router site:
import { createRevenueChart } from "../lib/graphjson";
export async function getStaticProps() {
const revenueChartUrl = await createRevenueChart();
return {
props: { revenueChartUrl }
};
}
export default function MetricsPage({ revenueChartUrl }) {
return (
<iframe
src={revenueChartUrl}
title="Daily revenue"
width="100%"
height="360"
loading="lazy"
style={{ border: 0 }}
/>
);
}
getStaticProps runs during the production build. The deployed page contains only the generated iframe URL, not the API key.
Other static generators follow the same pattern: call GraphJSON in the build process, then pass the returned URL into the generated HTML.
Configure the build environment#
Add the secret to the build platform:
GRAPHJSON_API_KEY=...
Do not prefix it with a framework convention that exposes variables to the browser, such as NEXT_PUBLIC_.
Security: A generated embed URL can be loaded by anyone who obtains it. Build-time generation protects the API key; it does not make the chart itself private.
Decide how builds should fail#
For a critical analytics page, fail the build if GraphJSON cannot generate the URL. For a non-critical marketing chart, you may prefer to render a placeholder and alert the team.
Whichever policy you choose, log only the status and redacted error. Do not print the API key or the complete generated URL into public build logs.
Update chart configuration#
Change the helper and rebuild the site when you need new filters, colors, or graph type. Because the iframe’s range can remain relative, the chart data stays current without rebuilding every day.