Quickstart
Add a governed Semaphor KPI to your own React app with the Data App SDK.
In this quickstart you add a live Revenue KPI to an existing React app. Your UI declares the query, and Semaphor runs it server-side with the same governed execution and security policies as your dashboards.
Before You Start
You need:
- a React 19 app built with Vite or Next.js;
- a Semaphor project with a semantic domain, for example a
commercedomain with afact_ordersdataset; - the project ID and project secret, available to your server (never to the browser).
Install the SDK
npm install react-semaphorThe Data App SDK ships as a subpath of the package:
import {
SemaphorDataAppProvider,
SemaphorDevtools,
semaphor,
useSemaphorQuery,
} from 'react-semaphor/data-app-sdk';Mint a project token on your server
Call the Token API from your backend with your project credentials. Identify the end user so Semaphor can apply their tenant and security policies.
export async function mintSemaphorToken() {
const response = await fetch('https://semaphor.cloud/api/v1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'project',
projectId: process.env.SEMAPHOR_PROJECT_ID,
projectSecret: process.env.SEMAPHOR_PROJECT_SECRET,
tenantId: 'tenant_acme',
endUserEmail: 'maria@acme.com',
}),
});
if (!response.ok) {
throw new Error(`Token request failed: ${response.status}`);
}
const { accessToken } = await response.json();
return accessToken as string;
}The browser receives only the short-lived accessToken. See Token Integration for tenant, end-user, and security options.
Keep secrets server-side
Never put your projectSecret or other long-lived credentials in browser code.
Wrap your app in the provider
Every SDK hook runs under SemaphorDataAppProvider. Pass it the token from your server.
Mint the token in a server component and pass it to a client component that holds the provider.
import { mintSemaphorToken } from '@/server/semaphor-token';
import { AnalyticsApp } from './analytics-app';
export default async function AnalyticsPage() {
const token = await mintSemaphorToken();
return <AnalyticsApp token={token} />;
}'use client';
import { SemaphorDataAppProvider } from 'react-semaphor/data-app-sdk';
import { RevenueKpi } from './revenue-kpi';
export function AnalyticsApp({ token }: { token: string }) {
return (
<SemaphorDataAppProvider token={token}>
<RevenueKpi />
</SemaphorDataAppProvider>
);
}Expose mintSemaphorToken() through an endpoint on your own backend, for example GET /api/semaphor-token, then fetch it when the app loads.
import { useEffect, useState } from 'react';
import { SemaphorDataAppProvider } from 'react-semaphor/data-app-sdk';
import { RevenueKpi } from './RevenueKpi';
export function App() {
const [token, setToken] = useState<string>();
useEffect(() => {
fetch('/api/semaphor-token')
.then((response) => response.json())
.then((body) => setToken(body.accessToken));
}, []);
if (!token) return <div>Connecting...</div>;
return (
<SemaphorDataAppProvider token={token}>
<RevenueKpi />
</SemaphorDataAppProvider>
);
}Define a source and a metric
A source points at a semantic dataset. Fields carry their source, so Semaphor knows which dataset owns each one.
import { semaphor } from 'react-semaphor/data-app-sdk';
export const orders = semaphor.source.semantic({
domainId: 'commerce',
datasetName: 'fact_orders',
});
export const revenue = semaphor.field.measure('revenue', {
source: orders,
aggregate: 'SUM',
});
export const revenueKpi = semaphor.metric({
id: 'revenue_kpi',
label: 'Revenue',
source: orders,
measures: [revenue],
primaryMeasure: revenue,
});Use the domainId and datasetName from your own project.
Render it with useSemaphorQuery
import { useSemaphorQuery } from 'react-semaphor/data-app-sdk';
import { revenueKpi } from './analytics';
export function RevenueKpi() {
const result = useSemaphorQuery(revenueKpi);
if (result.isLoading) return <div>Loading revenue...</div>;
if (result.error) return <div>{result.error.message}</div>;
return (
<section>
<h2>Revenue</h2>
<strong>{result.primaryValue}</strong>
</section>
);
}The hook sends the query to Semaphor, which applies the token's tenant and security policies before it runs. Render the result with any components you like, including your own design system.
Add DevTools in development
SemaphorDevtools shows each query, its inputs, execution status, and diagnostics. Turn on debug outside production and mount one DevTools instance inside the provider.
const isDev = process.env.NODE_ENV !== 'production'; // Vite: import.meta.env.DEV
<SemaphorDataAppProvider token={token} debug={isDev}>
<RevenueKpi />
<SemaphorDevtools buttonPosition="bottom-right" panelPosition="right" />
</SemaphorDataAppProvider>;DevTools render nothing when debug is off, so production users never see them.
Next Steps
- Configure API URLs, token refresh, and DevTools in Setup.
- Learn the building blocks in SDK Concepts.
- Build trends, tables, and pivots with Queries.
- Add filters with Filters And Inputs.
- Join labels and filter across datasets with Relationships.