Aller au contenu principal

La voie de requête wasm

La plupart des requêtes s'exécutent de notre côté : vous envoyez du SQL, nous l'autorisons, l'exécutons, et renvoyons des lignes typées. La voie wasm inverse cela. Vous demandez un plan au lieu de lignes — du Parquet présigné, un fichier par jeu de données, plus le SQL à exécuter dessus — et la requête s'exécute dans le navigateur du visiteur via DuckDB-WASM.

C'est la voie derrière un tableau de bord qui n'a besoin d'aucun identifiant : pas de clé d'API, pas de jeton d'intégration, pas de serveur à vous. C'est aussi la voie au compromis le plus tranché de toute la plateforme, et ce compromis n'est caché nulle part — c'est le même fait que « aucun secret à laisser fuir », lu de l'autre côté.

Comment la demander​

Un argument sur l'appel de requête sélectionne la voie.

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)

Via MCP c'est le même choix, écrit run_query(sql, type='wasm').

type vaut "backend" (la valeur par défaut) ou "wasm". Tout ce qui est au-dessus du choix de voie est inchangé : l'autorisation, la validation par alias uniquement et l'injection de filtres se font toutes côté serveur, avant la vérification de capacité. Demander wasm ne saute aucun contrôle. Un jeu de données que la clé ne peut pas atteindre est rejeté avant qu'un extrait ne soit matérialisé ou qu'une URL ne soit signée.

Branchez sur mode, jamais sur ce que vous avez demandé​

Demander wasm ne garantit pas de l'obtenir. Si l'un des jeux de données participants n'est pas compatible wasm, la requête s'exécute côté serveur exactement comme d'habitude — et le dit plutôt que d'échouer.

ChampSignification
modeCe qui s'est réellement exécuté : "backend" ou "wasm". C'est le champ sur lequel brancher.
fallbackReasonDéfini uniquement quand type="wasm" a été demandé et refusé, en nommant pourquoi.
planDéfini uniquement quand mode == "wasm".
columns / rowsVides quand mode == "wasm" — rien n'a été calculé.
Le tableau donne l'orthographe du protocole. Le SDK Python les expose en snake_case
(result.fallback_reason), le client TypeScript en camelCase, et les paramètres des outils MCP
sont en snake_case partout — donc type reste type en toute circonstance, mais fallbackReason
s'écrit fallback_reason en Python.

Un repli silencieux serait le pire comportement possible ici : votre code recevrait des lignes, croirait être sur la voie sans identifiants, et expédierait un build qui a besoin d'un identifiant qu'il n'a pas. Donc le repli n'est jamais silencieux, et la raison est toujours nommée.

Ce que contient un plan​

{
"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 est le SQL injecté — filtres appliqués, paramètres déjà échappés en littéraux. Ce n'est pas votre SQL brut, et c'est là tout le point : les deux voies calculent des résultats identiques, donc un tableau de bord ne peut pas signifier une chose servie depuis notre moteur et une autre chose servie depuis le navigateur.
  • sources[].alias est le cleanSQLName du jeu de données — exactement le nom de table que plan.sql référence, et le nom sous lequel le fichier Parquet doit être enregistré localement. Le fichier porte déjà des noms de colonnes propres.
  • sources[].url est présignée et en lecture seule. Elle est récupérable depuis n'importe quelle origine, et l'endpoint du plan l'est aussi.

Exécuter un plan, c'est : récupérer chaque url, l'enregistrer dans DuckDB-WASM comme une vue nommée alias, exécuter plan.sql. Un tableau de bord éjecté sur cette voie embarque un runner qui fait exactement cela et rien d'autre ; sa seule dépendance est @duckdb/duckdb-wasm, qui est sur npm.

Vous devez demander l'extrait avant qu'il n'existe​

C'est l'unique règle de séquencement, et la sauter produit une boucle qui ne termine jamais.

Demander type="wasm" est la seule chose qui construit l'extrait wasm d'un jeu de données. L'extrait est matérialisé paresseusement, par cette demande — ou par le chemin d'actualisation après qu'un jeu de données devient prêt — et nulle part ailleurs.

Donc si l'éjection rapporte dataLane.id == "token" avec la raison no-wasm-extract, le correctif est d'exécuter une requête par jeu de données nommé avec type="wasm" — n'importe quel SELECT trivial contre l'alias suffira — puis d'éjecter à nouveau. Un plan renvoyé signifie que l'extrait existe désormais ; pour cet usage vous pouvez ignorer entièrement le contenu du plan.

Publier ne cuit pas d'extrait. « Republier et rééjecter » bouclera indéfiniment.

La voie anonyme, et pourquoi elle n'est pas dans la référence de l'API​

La voie ci-dessus a besoin d'une clé d'API, parce que c'est vous qui construisez un tableau de bord. Ce qu'appelle le navigateur d'un visiteur est un endpoint différent, non authentifié : le plan wasm de la visualisation, à

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

Il est délibérément absent de la référence d'API générée et des deux SDK, et cette absence est une décision de conception plutôt qu'une omission. La route est anonyme et orientée navigateur ; il n'existe aucun appel par clé d'API pour elle, donc elle n'a pas sa place dans un SDK à clé d'API. Elle est exclue au niveau du framework, ce qui explique pourquoi vous ne la trouverez pas en parcourant la référence — la seule description qui en existe est cette page et le runner qui l'appelle.

Ses gardes sont identiques à ceux de la route de cache anonyme à côté d'elle : le compte doit exister et autoriser les tableaux de bord publics, et la visualisation doit être publique et publiée. Un tableau de bord privé ou protégé répond 404 ici. Rien là-dedans n'est une valeur par défaut côté client que vous pourriez surcharger.

Elle renvoie { sources: [...], unavailable: [...] }. Un jeu de données qui n'est pas compatible wasm est rapporté dans unavailable avec une raison plutôt que retiré silencieusement de sources — parce qu'un client qui recevrait une liste sources courte et aucune explication afficherait un tableau de bord partiel en le déclarant terminé.

Pourquoi un tableau de bord protégé ne pourra jamais être servi ainsi​

Les lignes d'un tableau de bord protégé sont délimitées par visionneur en réécrivant le SQL côté serveur. Un fichier Parquet présigné est la table entière. Il n'y a aucun moyen d'honorer un prédicat de lignes que le client pourrait simplement refuser d'exécuter — donc la voie n'est pas « pas encore implémentée » pour les tableaux de bord protégés ; elle est structurellement exclue, et elle le restera toujours.

Ce que vous sacrifiez​

Énoncez-le avant de construire, parce que c'est la moitié que vous ne pourrez pas changer ensuite.

L'extrait est indexé par jeu de données et instantané, pas par votre requête. Le même SELECT avec et sans WHERE renvoie le fichier identique. Votre prédicat ne le réduit pas. Conséquences, toutes la même conséquence :

  • Il contient toutes les lignes de chaque jeu de données que lit le tableau de bord.
  • C'est un seul fichier, le même fichier, pour tous ceux qui ouvrent la page.
  • La page n'émet aucune requête serveur, donc rien dans le chemin ne pourrait donner moins de lignes à un visiteur qu'à un autre.
  • Un filtre appliqué dans le navigateur change ce qui est dessiné, jamais ce qui a été livré — et les octets sont dans l'onglet réseau dans les deux cas.
  • Il sert l'instantané publié, pas une requête vivante. Rafraîchir ce qu'il montre veut dire republier.

En échange : aucune clé à laisser fuir, aucun jeton à renouveler, aucun backend à vous à faire tourner ou à payer, aucune limite de débit devant la page, et un tableau de bord qui continue de s'afficher depuis un hébergement statique.

À savoir aussi : le plafond de lignes ne s'applique pas ici. La voie côté serveur s'arrête à 100 000 lignes et positionne truncated, ce qui rend silencieusement faux chaque total calculé depuis le préfixe. Le Parquet de la voie wasm est le jeu de données entier par instantané, précisément parce qu'il est indexé par jeu de données plutôt que par votre résultat — donc c'est aussi la réponse honnête à un ensemble de résultats véritablement grand, pas seulement à un problème d'identifiants.

Quand choisir autre chose​

Vous avez besoin deVoie
lignes que vous allez lire et sur lesquelles raisonnerrun_query / client.query — la voie backend par défaut
beaucoup de lignes sur disquerun_query_to_file(output_path=...)
un tableau de bord public sans identifiant et sans backendwasm
que des visionneurs différents voient des lignes différentesun jeton d'intégration à portée restreinte portant des filters, ou du code serveur de confiance qui restreint le SQL
des données vivantes plutôt qu'un instantané publiéla voie backend, derrière un serveur que vous contrôlez

Un client à jeton restreint refuse type="wasm" d'emblée plutôt que de s'exécuter discrètement côté serveur : un appelant qui aurait demandé wasm et obtenu des lignes quand même n'aurait aucun moyen de s'en apercevoir. Ce refus est le même principe que fallbackReason — un changement de voie ne doit jamais être invisible.

Pour les deux câblages qui peuvent délimiter un visionneur, voir Le filtrage au niveau des lignes.