Pular para o conteúdo principal

A via de consulta wasm

A maioria das consultas roda do nosso lado: você envia SQL, nós autorizamos, executamos, e devolvemos linhas tipadas. A via wasm inverte isso. Você pede um plano em vez de linhas — Parquet pré-assinado, um arquivo por conjunto de dados, mais o SQL a rodar sobre ele — e a consulta executa no navegador do visitante através do DuckDB-WASM.

Esta é a via por trás de um painel que não precisa de credencial nenhuma: sem chave de API, sem token de incorporação, sem servidor seu. É também a via com o trade-off mais afiado de toda a plataforma, e o trade-off não está escondido em lugar nenhum — é o mesmo fato que "nenhum segredo para vazar", lido do outro lado.

Como pedi-la​

Um argumento na chamada de consulta seleciona a via.

from vibedasher import Vibedasher

client = Vibedasher(api_key="...")

result = client.query(sql="SELECT region AS dimension, SUM(price) AS value FROM sales GROUP BY 1", type="wasm")

if result.mode == "wasm":
run_locally(result.plan) # plan.sql + plan.sources
else:
print(result.rows, result.fallback_reason)

Por MCP é a mesma escolha, escrita run_query(sql, type='wasm').

type é "backend" (o padrão) ou "wasm". Tudo acima da escolha da via permanece igual: a autorização, a validação apenas por alias e a injeção de filtros continuam acontecendo no servidor, antes da checagem de capacidade. Pedir wasm não pula nenhum portão. Um conjunto de dados que a chave não pode alcançar é rejeitado antes de qualquer extrato ser materializado ou qualquer URL ser assinada.

Ramifique sobre mode, nunca sobre o que você pediu​

Pedir wasm não garante recebê-lo. Se algum conjunto de dados participante não for capaz de wasm, a consulta executa no servidor exatamente como de costume — e diz isso em vez de falhar.

CampoSignificado
modeO que realmente rodou: "backend" ou "wasm". Este é o campo sobre o qual ramificar.
fallbackReasonDefinido apenas quando type="wasm" foi pedido e recusado, nomeando por quê.
planDefinido apenas quando mode == "wasm".
columns / rowsVazios quando mode == "wasm" — nada foi computado.
A tabela traz a grafia do protocolo. O SDK Python os expõe em snake_case
(result.fallback_reason), o cliente TypeScript em camelCase, e os parâmetros das ferramentas
MCP são snake_case em todos os casos — portanto type é type em qualquer lugar, mas
fallbackReason vira fallback_reason em Python.

Um fallback silencioso seria o pior comportamento possível aqui: seu código receberia linhas, acreditaria estar na via sem credenciais, e enviaria um build que precisa de uma credencial que ele não tem. Então o fallback nunca é silencioso, e a razão é sempre nomeada.

O que um plano contém​

{
"mode": "wasm",
"plan": {
"sql": "SELECT region AS dimension, SUM(price) AS value FROM sales GROUP BY 1",
"sources": [
{ "alias": "sales", "datasetId": 12, "url": "https://...", "bytes": 4194304 }
]
}
}
  • plan.sql é o SQL injetado — filtros aplicados, parâmetros já escapados como literais. Não é o seu SQL bruto, e esse é o ponto: as duas vias computam resultados idênticos, então um painel não pode significar uma coisa servida pelo nosso motor e outra coisa servida pelo navegador.
  • sources[].alias é o cleanSQLName do conjunto de dados — exatamente o nome de tabela que o plan.sql referencia, e o nome sob o qual o arquivo Parquet deve ser registrado localmente. O arquivo já carrega nomes de coluna limpos.
  • sources[].url é pré-assinada e somente leitura. Ela é baixável de qualquer origem, e o endpoint do plano também.

Executar um plano é: buscar cada url, registrá-la no DuckDB-WASM como uma view chamada alias, rodar o plan.sql. Um painel ejetado nesta via traz um runner que faz exatamente isso e nada mais; sua única dependência é o @duckdb/duckdb-wasm, que está no npm.

Você tem de pedir o extrato antes de ele existir​

Esta é a única regra de sequência, e pulá-la produz um loop que nunca termina.

Pedir type="wasm" é a única coisa que constrói o extrato wasm de um conjunto de dados. O extrato é materializado de forma preguiçosa, por esse pedido — ou pelo caminho de atualização depois que um conjunto de dados fica pronto — e em nenhum outro lugar.

Então se a ejeção relatar dataLane.id == "token" com a razão no-wasm-extract, a correção é rodar uma consulta por conjunto de dados nomeado com type="wasm" — qualquer SELECT trivial contra o alias serve — e então ejetar de novo. Um plano devolvido significa que o extrato agora existe; para esse propósito você pode ignorar completamente o conteúdo do plano.

Publicar não assa um extrato. "Republicar e reejetar" vai rodar em círculos para sempre.

A via anônima, e por que ela não está na referência da API​

A via acima precisa de uma chave de API, porque é você construindo um painel. O que o navegador de um visitante chama é um endpoint diferente, não autenticado: o plano wasm da visualização, em

GET /v1/public/viz/{account_slug}/{viz_slug}/wasm-plan

Ele está deliberadamente ausente da referência de API gerada e dos dois SDKs, e essa ausência é uma decisão de projeto, não uma omissão. A rota é anônima e voltada ao navegador; não existe chamada por chave de API para ela, então ela não tem lugar em um SDK de chave de API. Ela é excluída no nível do framework, e é por isso que você não vai encontrá-la navegando a referência — a única descrição dela é esta página e o runner que a chama.

Suas proteções são idênticas às da rota de cache anônima ao lado dela: a conta precisa existir e permitir painéis públicos, e a visualização precisa ser pública e publicada. Um painel privado ou protegido responde 404 aqui. Nada nisso é um padrão do lado do cliente que você possa sobrescrever.

Ela retorna { sources: [...], unavailable: [...] }. Um conjunto de dados que não é capaz de wasm é relatado em unavailable com uma razão em vez de ser silenciosamente removido de sources — porque um cliente que recebesse uma lista sources curta e nenhuma explicação renderizaria um painel parcial e o daria por pronto.

Por que um painel protegido nunca pode ser servido desta forma​

As linhas de um painel protegido são delimitadas por visualizador reescrevendo o SQL no servidor. Um arquivo Parquet pré-assinado é a tabela inteira. Não há maneira de honrar um predicado de linha que o cliente simplesmente poderia se recusar a rodar — então a via não está "ainda não implementada" para painéis protegidos; ela é estruturalmente excluída, e sempre será.

O que você está sacrificando​

Declare isso antes de construir, porque é a metade que você não pode mudar depois.

O extrato é indexado por conjunto de dados e snapshot, não pela sua consulta. O mesmo SELECT com e sem WHERE devolve o arquivo idêntico. Seu predicado não o encolhe. Consequências, todas elas a mesma consequência:

  • Ele contém todas as linhas de cada conjunto de dados que o painel lê.
  • É um arquivo, o mesmo arquivo, para todos que abrem a página.
  • A página não emite consulta ao servidor, então nada no caminho poderia dar a um visualizador menos linhas do que a outro.
  • Um filtro aplicado no navegador muda o que é desenhado, nunca o que foi entregue — e os bytes estão na aba de rede de qualquer forma.
  • Ele serve o snapshot publicado, não uma consulta viva. Atualizar o que ele mostra significa republicar.

Em troca: nenhuma chave para vazar, nenhum token para renovar, nenhum backend seu para rodar ou pagar, nenhum limite de taxa na frente da página, e um painel que continua renderizando a partir de uma hospedagem estática.

Também vale saber: o limite de linhas não se aplica aqui. A via do lado do servidor para em 100.000 linhas e define truncated, o que torna silenciosamente errado todo total calculado a partir do prefixo. O Parquet da via wasm é o conjunto de dados inteiro por snapshot, precisamente porque é indexado por conjunto de dados em vez de pelo seu resultado — então ela é também a resposta honesta para um conjunto de resultados genuinamente grande, não só para um problema de credencial.

Quando escolher outra coisa​

Você precisa deVia
linhas que você vai ler e sobre as quais raciocinarrun_query / client.query — a via backend padrão
muitas linhas em discorun_query_to_file(output_path=...)
um painel público sem credencial e sem backendwasm
que visualizadores diferentes vejam linhas diferentesum token de incorporação com escopo carregando filters, ou código de servidor confiável que restringe o SQL
dados ao vivo em vez de um snapshot publicadoa via backend, atrás de um servidor que você controla

Um cliente com token com escopo recusa type="wasm" de saída em vez de rodar silenciosamente no servidor: quem pedisse wasm e recebesse linhas de qualquer jeito não teria como perceber. Essa recusa é o mesmo princípio do fallbackReason — uma mudança de via nunca deve ser invisível.

Para as duas ligações que podem delimitar um visualizador, veja Filtragem em nível de linha.