Logosulfite.app
rafagazani/sulfite 999999

Geração de Relatórios

Endpoints de geração síncrona e assíncrona do sulfite_server

Geração de Relatórios#

O servidor oferece três formas de gerar relatórios, cada uma com suporte a modo síncrono e assíncrono.

Formatos suportados#

FormatoContent-TypeExtensão
pdfapplication/pdf.pdf
htmltext/html.html
csvtext/csv.csv
excel application/vnd.openxmlformats-officedocument.spreadsheetml.sheet .xlsx

Formatos inválidos retornam 400 invalid_format.

Endpoints de geração#

Gerar de relatório salvo — POST /api/v1/reports/:id/generate#

Permissão: canGenerate

Query params: ?format=pdf (default: pdf)

Body (opcional):

{
  "params": { "startDate": "2026-01-01" },
  "dataPayload": { "sales": [{ "product": "A", "amount": 100 }] }
}
CampoDescrição
paramsParâmetros do relatório (substituem variáveis no template)
dataPayloadDados inline (sobrescrevem datasources externos)

Resposta 200: Binário do arquivo com Content-Disposition: inline; filename="report-name.pdf".

StatusErroQuando
404not_foundRelatório não encontrado
413 output_too_large Output excede MAX_OUTPUT_SIZE (default 200 MB)
422invalid_reportDefinição inválida
422generation_failedErro durante renderização

Gerar inline — POST /api/v1/generate#

Permissão: canGenerate

Body:

{
  "definition": { "reportName": "Inline Report", "bands": [...] },
  "format": "pdf",
  "params": { "key": "value" },
  "dataPayload": { "key": [...] }
}
CampoObrigatórioDescrição
definitionSimObjeto JSON ou string JSON da definição
formatNãoFormato de saída (default: pdf)
paramsNãoParâmetros do relatório
dataPayloadNãoDados inline
StatusErroQuando
400 validation_error definition ausente
413output_too_largeOutput excede limite
422invalid_definitionDefinição malformada
422generation_failedErro durante renderização

Gerar de pacote — POST /api/v1/generate/package#

Permissão: canGenerate

Content-Type: multipart/form-data

PartTipoObrigatórioDescrição
package ou file binário Sim Bytes do pacote .sulfite
formattextoNãoFormato (default: pdf)
params JSON string Não Parâmetros ({"key":"value"})

Limite de upload: 50 MB.

StatusErroQuando
400validation_errorPacote ausente
413output_too_largeOutput excede limite
422invalid_packagePacote malformado
422generation_failedErro durante renderização

Modo assíncrono#

Para relatórios grandes ou lentos, use o modo assíncrono enviando o header:

Prefer: respond-async

O servidor retorna imediatamente com um job ID e o relatório é processado em background.

Resposta 202#

{
  "jobId": "a1b2c3d4e5f6...",
  "status": "queued",
  "poll": "/api/v1/jobs/a1b2c3d4e5f6...",
  "stream": "/api/v1/jobs/a1b2c3d4e5f6.../stream",
  "output": "/api/v1/jobs/a1b2c3d4e5f6.../output"
}
- Relatórios com muitos dados (milhares de linhas) - Datasources externos lentos - Quando a UI precisa mostrar progresso em tempo real

Configuração da fila de jobs#

ParâmetroValor
Máximo de jobs simultâneos4
Máximo de jobs pendentes100
Máximo pendentes por IP10
Memória máxima de resultados512 MB
Retenção de resultados30 minutos

Fila cheia — 429 queue_full#

Se a fila de jobs está cheia, retorna 429 com error: "queue_full".

Jobs API#

Propriedade de jobs#

Jobs armazenam o subject do access token. Um requester só pode acessar seus próprios jobs. Superusers (sem subject) acessam todos. Acesso não autorizado retorna 404 (não 403) para evitar enumeração.

Status do job — GET /api/v1/jobs/:id#

Resposta 200:

{
  "id": "hex-id",
  "status": "running",
  "progress": 45,
  "format": "pdf",
  "createdAt": "2026-04-06T12:00:00.000Z",
  "completedAt": null,
  "error": null,
  "reportName": "Sales Report",
  "outputSize": null,
  "subject": "user@example.com"
}
CampoPresença
status Sempre: queued, running, done, failed
progressSempre: 0–100
completedAtApenas quando terminal
errorApenas quando failed
outputSizeApenas quando done (bytes)

Stream SSE — GET /api/v1/jobs/:id/stream#

Content-Type: text/event-stream

Recebe eventos em tempo real conforme o job progride:

event: queued
data: {"id":"...","status":"queued","progress":0}

event: running
data: {"id":"...","status":"running","progress":45}

event: done
data: {"id":"...","status":"done","progress":100}

Os nomes dos eventos correspondem ao status: queued, running, done, failed. O stream fecha quando o job atinge estado terminal.

Se o job já está completo quando o stream é aberto, um único evento é enviado e o stream fecha imediatamente.


Download do output — GET /api/v1/jobs/:id/output#

Resposta 200: Binário do arquivo com o Content-Type do formato.

StatusErroQuando
404not_foundJob não encontrado
409 not_ready Job não está done (retorna status atual)

Exemplo: geração assíncrona com SSE#

// 1. Iniciar geração assíncrona
const res = await fetch('/api/v1/reports/abc123/generate?format=pdf', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ...',
    'Prefer': 'respond-async'
  }
});
const { jobId, stream } = await res.json();

// 2. Acompanhar progresso via SSE
const events = new EventSource(stream);
events.addEventListener('running', (e) => {
  const { progress } = JSON.parse(e.data);
  console.log(`Progresso: ${progress}%`);
});
events.addEventListener('done', async () => {
  events.close();
  // 3. Baixar resultado
  const output = await fetch(`/api/v1/jobs/${jobId}/output`, {
    headers: { 'Authorization': 'Bearer ...' }
  });
  const blob = await output.blob();
});
events.addEventListener('failed', (e) => {
  events.close();
  const { error } = JSON.parse(e.data);
  console.error('Falha:', error);
});