Zum Hauptinhalt springen

Die Wasm-Abfragespur

Die meisten Abfragen laufen auf unserer Seite: Sie senden SQL, wir autorisieren es, führen es aus und geben typisierte Zeilen zurück. Die Wasm-Spur kehrt das um. Sie bitten um einen Plan statt um Zeilen — vorsigniertes Parquet, eine Datei pro Datensatz, plus das SQL, das darüber laufen soll — und die Abfrage wird im Browser des Besuchers durch DuckDB-WASM ausgeführt.

Das ist die Spur hinter einem Dashboard, das überhaupt keine Zugangsdaten braucht: keinen API-Schlüssel, kein Embed-Token, keinen eigenen Server. Es ist auch die Spur mit dem schärfsten Kompromiss der ganzen Plattform, und der Kompromiss ist nirgends versteckt — er ist dieselbe Tatsache wie „kein Geheimnis, das durchsickern kann", nur von der anderen Seite gelesen.

Danach fragen​

Ein Argument im Abfrageaufruf wählt die Spur.

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)

Über MCP ist es dieselbe Wahl, geschrieben run_query(sql, type='wasm').

type ist "backend" (der Standard) oder "wasm". Alles oberhalb der Spurwahl ist unverändert: Autorisierung, Alias-only-Validierung und Filterinjektion geschehen alle weiterhin serverseitig, vor der Fähigkeitsprüfung. Nach Wasm zu fragen überspringt kein Gate. Ein Datensatz, den der Schlüssel nicht erreichen kann, wird abgelehnt, bevor irgendein Auszug materialisiert oder irgendeine URL signiert wird.

Verzweigen Sie auf mode, nie auf das, wonach Sie gefragt haben​

Nach Wasm zu fragen garantiert nicht, es zu bekommen. Wenn irgendein beteiligter Datensatz nicht Wasm-fähig ist, wird die Abfrage serverseitig genau wie üblich ausgeführt — und sagt das, statt zu scheitern.

FeldBedeutung
modeWas tatsächlich gelaufen ist: "backend" oder "wasm". Das ist das Feld, auf das zu verzweigen ist.
fallbackReasonNur gesetzt, wenn type="wasm" angefragt und abgelehnt wurde, und nennt warum.
planNur gesetzt, wenn mode == "wasm".
columns / rowsLeer, wenn mode == "wasm" — es wurde nichts berechnet.
Die Tabelle nennt die Schreibweise des Protokolls. Das Python-SDK stellt sie in snake_case bereit
(result.fallback_reason), der TypeScript-Client in camelCase, und die Parameter der
MCP-Tools sind durchgehend snake_case — type bleibt also überall type, aber fallbackReason
heißt in Python fallback_reason.

Ein stiller Fallback wäre hier das schlimmstmögliche Verhalten: Ihr Code würde Zeilen erhalten, glauben, er sei auf der Spur ohne Zugangsdaten, und einen Build ausliefern, der ein Zugangsdatum braucht, das er nicht hat. Deshalb ist der Fallback nie still, und der Grund wird immer genannt.

Was ein Plan enthält​

{
"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 ist das injizierte SQL — Filter angewandt, Parameter schon als maskierte Literale. Es ist nicht Ihr Roh-SQL, und das ist der Punkt: Beide Spuren berechnen identische Ergebnisse, sodass ein Dashboard nicht eine Sache bedeuten kann, wenn es aus unserer Engine kommt, und eine andere, wenn es aus dem Browser kommt.
  • sources[].alias ist der cleanSQLName des Datensatzes — genau der Tabellenname, auf den plan.sql verweist, und der Name, unter dem die Parquet-Datei lokal registriert werden muss. Die Datei trägt schon saubere Spaltennamen.
  • sources[].url ist vorsigniert und nur lesbar. Sie ist von jedem Origin abrufbar, und der Plan-Endpunkt selbst ebenso.

Einen Plan auszuführen heißt: jede url abrufen, sie in DuckDB-WASM als View mit dem Namen alias registrieren, plan.sql ausführen. Ein ausgeworfenes Dashboard auf dieser Spur bringt einen Runner mit, der genau das und nichts anderes tut; seine einzige Abhängigkeit ist @duckdb/duckdb-wasm, die auf npm liegt.

Sie müssen um den Auszug bitten, bevor er existiert​

Das ist die eine Reihenfolgeregel, und sie zu überspringen erzeugt eine Schleife, die nie endet.

Nach type="wasm" zu fragen ist das Einzige, was den Wasm-Auszug eines Datensatzes erzeugt. Der Auszug wird verzögert materialisiert, durch diese Bitte — oder durch den Aktualisierungspfad, nachdem ein Datensatz bereit wird — und nirgends sonst.

Wenn also Eject dataLane.id == "token" mit dem Grund no-wasm-extract meldet, ist die Behebung, eine Abfrage pro benanntem Datensatz mit type="wasm" auszuführen — jedes triviale SELECT gegen den Alias reicht — und dann erneut auszuwerfen. Ein zurückgegebener Plan bedeutet, dass der Auszug nun existiert; für diesen Zweck können Sie den Inhalt des Plans völlig ignorieren.

Veröffentlichen backt keinen Auszug. „Erneut veröffentlichen und erneut auswerfen" läuft für immer im Kreis.

Die anonyme Spur, und warum sie nicht in der API-Referenz steht​

Die Spur oben braucht einen API-Schlüssel, denn es sind Sie, die ein Dashboard bauen. Was der Browser eines Besuchers aufruft, ist ein anderer, nicht authentifizierter Endpunkt: der Wasm-Plan der Visualisierung, unter

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

Er fehlt bewusst in der generierten API-Referenz und in beiden SDKs, und dieses Fehlen ist eine Designentscheidung und keine Auslassung. Die Route ist anonym und browserseitig; es gibt keinen API-Schlüssel-Aufruf dafür, also hat sie in einem API-Schlüssel-SDK keinen Platz. Sie ist auf Framework-Ebene ausgeschlossen, weshalb Sie sie nicht finden werden, wenn Sie die Referenz durchblättern — die einzige Beschreibung davon ist diese Seite und der Runner, der sie aufruft.

Ihre Wächter sind identisch mit der anonymen Cache-Route daneben: Das Konto muss existieren und öffentliche Dashboards erlauben, und die Visualisierung muss öffentlich und veröffentlicht sein. Ein privates oder geschütztes Dashboard antwortet hier mit 404. Nichts daran ist ein clientseitiger Standard, den Sie übersteuern könnten.

Er gibt { sources: [...], unavailable: [...] } zurück. Ein Datensatz, der nicht Wasm-fähig ist, wird in unavailable mit einem Grund gemeldet, statt stillschweigend aus sources entfernt zu werden — denn ein Client, der eine kurze sources-Liste und keine Erklärung erhielte, würde ein unvollständiges Dashboard rendern und es als fertig bezeichnen.

Warum ein geschütztes Dashboard so niemals ausgeliefert werden kann​

Die Zeilen eines geschützten Dashboards werden pro Betrachter eingeschränkt, indem das SQL serverseitig umgeschrieben wird. Eine vorsignierte Parquet-Datei ist die ganze Tabelle. Es gibt keine Möglichkeit, ein Zeilenprädikat einzuhalten, dessen Ausführung der Client einfach verweigern könnte — die Spur ist für geschützte Dashboards also nicht „noch nicht implementiert"; sie ist strukturell ausgeschlossen, und wird es immer sein.

Was Sie aufgeben​

Sagen Sie es, bevor Sie bauen, denn es ist die Hälfte, die Sie danach nicht mehr ändern können.

Der Auszug hat als Schlüssel Datensatz und Snapshot, nicht Ihre Abfrage. Dasselbe SELECT mit und ohne WHERE gibt die identische Datei zurück. Ihr Prädikat verkleinert sie nicht. Konsequenzen, alle dieselbe Konsequenz:

  • Er enthält jede Zeile jedes Datensatzes, den das Dashboard liest.
  • Er ist eine Datei, dieselbe Datei, für jeden, der die Seite öffnet.
  • Die Seite stellt keine Serverabfrage, also könnte nichts im Pfad einem Betrachter weniger Zeilen geben als einem anderen.
  • Ein im Browser angewandter Filter ändert, was gezeichnet wird, nie, was ausgeliefert wurde — und die Bytes stehen so oder so im Netzwerk-Tab.
  • Er liefert den veröffentlichten Snapshot, keine lebende Abfrage. Das Gezeigte zu aktualisieren bedeutet erneut zu veröffentlichen.

Im Gegenzug: kein Schlüssel, der durchsickern kann, kein Token, das erneuert werden muss, kein eigenes Backend, das Sie betreiben oder bezahlen müssen, kein Rate-Limit vor der Seite, und ein Dashboard, das aus einem statischen Host weiter rendert.

Auch wissenswert: Die Zeilenobergrenze gilt hier nicht. Die serverseitige Spur hört bei 100.000 Zeilen auf und setzt truncated, was jede aus dem Präfix berechnete Summe stillschweigend falsch macht. Das Parquet der Wasm-Spur ist der ganze Datensatz pro Snapshot, genau weil es als Schlüssel den Datensatz statt Ihr Ergebnis hat — sie ist also auch die ehrliche Antwort auf eine wirklich große Ergebnismenge, nicht nur auf ein Zugangsdatenproblem.

Wann Sie etwas anderes wählen sollten​

Sie brauchenSpur
Zeilen, die Sie lesen und über die Sie nachdenken werdenrun_query / client.query — die Standard-Backend-Spur
viele Zeilen auf der Festplatterun_query_to_file(output_path=...)
ein öffentliches Dashboard ohne Zugangsdaten und ohne Backendwasm
dass verschiedene Betrachter verschiedene Zeilen sehenein gescopetes Embed-Token mit filters, oder vertrauenswürdiger Servercode, der das SQL verengt
lebende Daten statt eines veröffentlichten Snapshotsdie Backend-Spur hinter einem Server, den Sie kontrollieren

Ein Client mit gescopetem Token lehnt type="wasm" direkt ab, statt stillschweigend serverseitig zu laufen: Ein Aufrufer, der nach Wasm fragte und trotzdem Zeilen bekam, hätte keine Möglichkeit, es zu bemerken. Diese Ablehnung folgt demselben Prinzip wie fallbackReason — ein Spurwechsel darf niemals unsichtbar sein.

Für die beiden Verdrahtungen, die einen Betrachter einschränken können, siehe Zeilenbasierte Filterung.