Abra seu primeiro relatório
Emita um token no backend do host e monte o editor React com dados reais do seu produto.
Abrir como Markdown1. Instale os dois lados da integração
O pacote Node faz a troca segura de credenciais no servidor. O pacote React renderiza o widget no navegador. Você precisa dos dois em uma aplicação Next.js completa.
pnpm add @rhizzalab/report-react @rhizzalab/report-node2. Emita o token no servidor
Crie uma Route Handler protegida pela autenticação do seu produto. @rhizzalab/report-node valida a requisição antes da rede e devolve erros tipados sem expor o segredo.
import "server-only"
import { createReportPlatformClient } from "@rhizzalab/report-node"
import { NextResponse } from "next/server"
import { requireAuthenticatedUser } from "@/lib/auth"
export const runtime = "nodejs"
function requiredEnv(name: string): string {
const value = process.env[name]
if (!value) throw new Error("Variável ausente: " + name)
return value
}
const reports = createReportPlatformClient({
clientId: requiredEnv("RHIZZADOCS_CLIENT_ID"),
clientSecret: requiredEnv("RHIZZADOCS_CLIENT_SECRET"),
apiBaseUrl: process.env.RHIZZADOCS_API_URL,
})
export async function POST(request: Request) {
// Esta função pertence ao seu host: valide a sessão antes da troca.
const user = await requireAuthenticatedUser(request)
const token = await reports.issueEditorToken({
userId: user.id,
workspaceId: user.companyId,
reports: [
{
reportType: "acme.relatorio-mensal",
scopes: ["editor:read", "templates:read", "export:pdf"],
},
],
})
return NextResponse.json(token)
}3. Monte o editor no cliente
Crie um único ReportClient, forneça-o por contexto e renderize o ReportEditor. O callback onAuthRequired é o ponto para pedir outro JWT sem colocar a credencial permanente no browser.
"use client"
import {
ReportEditor,
ReportSdkProvider,
createReportClient,
type ReportEditorProps,
} from "@rhizzalab/report-react"
import { useCallback, useEffect, useMemo, useState } from "react"
type ReportScreenProps = Pick<
ReportEditorProps,
"data" | "documentKey" | "reportType"
>
export function ReportScreen(props: ReportScreenProps) {
const client = useMemo(
() =>
createReportClient({
apiBaseUrl: process.env.NEXT_PUBLIC_RHIZZADOCS_API_URL,
}),
[],
)
const [token, setToken] = useState<string | null>(null)
const [error, setError] = useState<string | null>(null)
const loadToken = useCallback(async () => {
setError(null)
try {
const response = await fetch("/api/rhizzadocs/token", { method: "POST" })
if (!response.ok) throw new Error("Não foi possível abrir o editor.")
const body = (await response.json()) as { accessToken: string }
setToken(body.accessToken)
} catch (cause: unknown) {
setError(cause instanceof Error ? cause.message : "Falha inesperada.")
}
}, [])
useEffect(() => {
void loadToken()
}, [loadToken])
if (error) return <p role="alert">{error}</p>
if (!token) return <p aria-live="polite">Preparando o editor…</p>
return (
<ReportSdkProvider client={client}>
<ReportEditor
token={token}
reportType={props.reportType}
data={props.data}
documentKey={props.documentKey}
onAuthRequired={() => void loadToken()}
onError={(editorError) => {
if (editorError.code !== "AUTH_REQUIRED") {
setError(editorError.message)
}
}}
/>
</ReportSdkProvider>
)
}4. Passe os dados do host
O reportType aponta para uma definição publicada. data deve corresponder ao JSON Schema dessa definição. Com documentKey, o editor restaura e salva um documento de trabalho para aquela identidade de negócio.
import { ReportScreen } from "@/components/report-screen"
export default async function ReportPage() {
const report = await loadReport("report_42")
return (
<ReportScreen
reportType="acme.relatorio-mensal"
documentKey={report.id}
data={{
customer: report.customer,
items: report.items,
total: report.total,
}}
/>
)
}