Tutorial: Relatório com Supabase/PostgREST
Neste tutorial você criará um relatório que busca dados de uma API Supabase (PostgREST) usando o PostgRestDataSourceResolver, com autenticação via ConnectionEntry.http e headers configuráveis.
O que você vai aprender:
- Declarar um data source externo com
driver: "postgrest" - Usar
ConnectionEntry.httpcomdefaultHeaderspara autenticação - Montar queries com a sintaxe PostgREST (select, filtros, order)
- Configurar o
PostgRestDataSourceResolverdo pacotesulfite_datasources - Usar um proxy para Flutter Web
Pré-requisitos
- Sulfite instalado (Instalação →)
- Instale os pacotes necessários:
dependencies:
sulfite_core: ^<versão>
sulfite_datasources: ^<versão>
sulfite_report_manager: ^<versão> # para ConnectionEntry e Registry- Uma API Supabase ou PostgREST acessível (ou use o proxy de demo:
https://sulfite-api-example.onrender.com/supabase)
Passo 1 — Configurar a conexão
Crie uma ConnectionEntry.http com a URL base da API e os headers de autenticação:
import 'package:sulfite_core/sulfite_core.dart';
import 'package:sulfite_datasources/sulfite_datasources.dart';
final registry = MutableConnectionRegistry([
ConnectionEntry.http(
name: 'supabase_prod',
baseUrl: 'https://xyz.supabase.co/rest/v1',
defaultHeaders: {
'apikey': 'your-anon-key',
'Authorization': 'Bearer your-anon-key',
},
),
]);Os defaultHeaders são injetados automaticamente em todas as requisições feitas por essa conexão. Para Supabase, os headers apikey e Authorization são obrigatórios.
Passo 2 — Declarar o data source no relatório
O data source referencia a conexão pelo connectionRef e define a query na sintaxe PostgREST:
{
"dataSources": [
{
"id": "vendas",
"type": "list",
"source": "external",
"driver": "postgrest",
"connectionRef": "supabase_prod",
"query": "vendas?select=id,produto,valor,data&order=data.desc",
"schema": {
"id": "integer",
"produto": "string",
"valor": "number",
"data": "string"
}
}
]
}Anatomia da query PostgREST
O campo query usa a sintaxe de query string do PostgREST:
vendas?select=id,produto,valor,data&status=eq.active&valor=gt.100&order=data.desc&limit=50
│ │ │ │ │ │
│ └─ colunas └─ filtro = └─ filtro > └─ ordenação └─ limite
└─ nome da tabelaOperadores disponíveis:
| Operador | Exemplo | Descrição |
|---|---|---|
eq | status=eq.active | Igual a |
neq | status=neq.cancelled | Diferente de |
gt, gte | valor=gt.100 | Maior que / Maior ou igual |
lt, lte | valor=lt.1000 | Menor que / Menor ou igual |
like | nome=like.*silva* | Busca textual (case-sensitive) |
ilike | nome=ilike.*silva* | Busca textual (case-insensitive) |
in | status=in.(active,pending) | Está na lista |
order | order=data.desc | Ordenação |
limit | limit=50 | Limitar resultados |
select | select=id,nome,total | Selecionar colunas |
Passo 3 — Criar o resolver e gerar o relatório
import 'package:sulfite_core/sulfite_core.dart';
import 'package:sulfite_datasources/sulfite_datasources.dart';
final engine = SulfiteEngineImpl();
final report = await engine.parseReport(reportJson);
final resolver = PostgRestDataSourceResolver(registry: registry);
final pdfBytes = await engine.generate(
report,
resolvers: [resolver],
format: 'pdf',
);
await File('vendas.pdf').writeAsBytes(pdfBytes);Ordem dos resolvers
Se o relatório tiver data sources REST e PostgREST, coloque o PostgRestDataSourceResolver antes do RestDataSourceResolver, pois ambos lidam com HTTP mas o PostgREST monta queries otimizadas:
resolvers: [
PostgRestDataSourceResolver(registry: registry),
RestDataSourceResolver(),
],Passo 4 — Usar com o Studio
Para preview em tempo real no Studio, passe os resolvers no SulfiteDesigner:
SulfiteDesigner(
initialReport: report,
dataSourceResolvers: [
PostgRestDataSourceResolver(registry: registry),
RestDataSourceResolver(),
],
)Gerenciar conexões no app
Use o ConnectionManagerWidget para permitir que o usuário configure conexões HTTP visualmente — incluindo o editor de headers:
ConnectionManagerWidget(registry: registry)O formulário de conexão HTTP exibe campos para nome, URL base e um editor de headers (pares chave-valor). Os headers são salvos no defaultHeaders da ConnectionEntry.http.
Modo proxy (Flutter Web)
Em Flutter Web, acessar a API Supabase diretamente pode expor a anon key no client-side. O padrão recomendado é usar um proxy no seu backend:
Flutter Web → Seu Backend (proxy) → Supabase PostgREST
headers didáticos headers reais injetadosConfiguração do proxy
No backend (ex: Dart com Relic), crie um handler que:
- Recebe a requisição do client
- Injeta os headers reais do Supabase (
apikey,Authorizationcom a chave real) - Encaminha para a API Supabase
- Retorna a resposta ao client
Usar o proxy no relatório
{
"id": "vendas",
"type": "list",
"source": "external",
"driver": "postgrest",
"connectionRef": "supabase_proxy",
"query": "vendas?select=*&order=data.desc"
}final registry = MutableConnectionRegistry([
ConnectionEntry.http(
name: 'supabase_proxy',
baseUrl: 'https://my-api.onrender.com/supabase',
defaultHeaders: {
'apikey': 'demo',
'Authorization': 'Bearer demo',
},
),
]);O proxy recebe os headers didáticos e os substitui pelos reais antes de encaminhar ao Supabase.
Modo URL inline (alternativa)
Para protótipos rápidos, é possível embutir a URL e headers diretamente no data source:
{
"id": "vendas",
"type": "list",
"source": "external",
"url": "https://xyz.supabase.co/rest/v1",
"headers": {
"apikey": "your-anon-key",
"Authorization": "Bearer your-anon-key"
},
"query": "vendas?select=*&order=data.desc"
}Segurança
Evite embutir credenciais no JSON em produção. Use connectionRef com um ConnectionRegistry gerenciado pela aplicação.
Próximo passo
- Data Sources → para a referência completa de propriedades
- Report Manager → para gerenciar conexões no app
- Relatório com API REST → para fontes REST genéricas