# La corsia di query wasm

La maggior parte delle query gira dalla nostra parte: tu invii l'SQL, noi lo autorizziamo, lo eseguiamo e restituiamo righe tipizzate. **La corsia wasm ribalta questo.** Chiedi un *piano* invece di righe — Parquet prefirmato, un file per dataset, più l'SQL da eseguire su di esso — e la query viene eseguita nel browser del visitatore attraverso DuckDB-WASM.

È la corsia dietro una dashboard che non ha bisogno di **alcuna credenziale**: nessuna chiave API, nessun token di incorporazione, nessun server tuo. È anche la corsia con il compromesso più netto di tutta la piattaforma, e il compromesso non è nascosto da nessuna parte — è lo stesso fatto di "nessun segreto da far trapelare", letto dall'altro lato.

## Come chiederla[​](#come-chiederla "Link diretto a Come chiederla")

Un argomento nella chiamata di query seleziona la corsia.

```
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)
```

Su MCP è la stessa scelta, scritta `run_query(sql, type='wasm')`.

`type` è `"backend"` (il default) o `"wasm"`. Tutto ciò che sta sopra la scelta della corsia è invariato: autorizzazione, validazione dei soli alias e iniezione dei filtri avvengono ancora tutte lato server, **prima** del controllo di capacità. Chiedere wasm non salta alcun controllo. Un dataset che la chiave non può raggiungere viene rifiutato prima che qualsiasi estratto sia materializzato o qualsiasi URL firmato.

## Ramifica su `mode`, mai su ciò che hai chiesto[​](#ramifica-su-mode-mai-su-ciò-che-hai-chiesto "Link diretto a ramifica-su-mode-mai-su-ciò-che-hai-chiesto")

**Chiedere wasm non garantisce di ottenerlo.** Se qualunque dataset coinvolto non è abilitato a wasm, la query viene eseguita lato server esattamente come di consueto — e lo dichiara invece di fallire.

| Campo                                                                                              | Significato                                                                                    |
| -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `mode`                                                                                             | Cosa è **effettivamente** girato: `"backend"` o `"wasm"`. È questo il campo su cui ramificare. |
| `fallbackReason`                                                                                   | Impostato solo quando `type="wasm"` è stato chiesto e rifiutato, e nomina il **perché**.       |
| `plan`                                                                                             | Impostato solo quando `mode == "wasm"`.                                                        |
| `columns` / `rows`                                                                                 | Vuoti quando `mode == "wasm"` — non è stato calcolato nulla.                                   |
| La tabella riporta la grafia del protocollo. L'SDK Python le espone in snake\_case                 |                                                                                                |
| (`result.fallback_reason`), il client TypeScript in camelCase, e i *parametri* degli strumenti MCP |                                                                                                |
| sono snake\_case in ogni caso — quindi `type` resta `type` ovunque, ma `fallbackReason` in Python  |                                                                                                |
| si scrive `fallback_reason`.                                                                       |                                                                                                |

Un fallback silenzioso sarebbe qui il comportamento peggiore possibile: il tuo codice riceverebbe righe, crederebbe di essere sulla corsia senza credenziali, e spedirebbe una build che ha bisogno di una credenziale che non ha. Quindi il fallback non è mai silenzioso, e la ragione è sempre nominata.

## Cosa contiene un piano[​](#cosa-contiene-un-piano "Link diretto a Cosa contiene un piano")

```
{

  "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` è l'SQL *iniettato*** — filtri applicati, parametri già letterali con escape. Non è il tuo SQL grezzo, ed è questo il punto: entrambe le corsie calcolano risultati identici, così che una dashboard non possa significare una cosa servita dal nostro motore e un'altra cosa servita dal browser.
* **`sources[].alias` è il `cleanSQLName` del dataset** — esattamente il nome della tabella a cui `plan.sql` fa riferimento, e il nome sotto cui il file Parquet deve essere registrato in locale. Il file porta già nomi di colonna puliti.
* **`sources[].url` è prefirmato e in sola lettura.** È scaricabile da qualsiasi origine, e così è anche l'endpoint del piano stesso.

Eseguire un piano è: scaricare ogni `url`, registrarla in DuckDB-WASM come vista chiamata `alias`, eseguire `plan.sql`. Una dashboard espulsa su questa corsia spedisce un runner che fa esattamente questo e nient'altro; la sua unica dipendenza è `@duckdb/duckdb-wasm`, che è su npm.

## Devi chiedere l'estratto prima che esista[​](#you-have-to-ask-for-the-extract-before-it-exists "Link diretto a Devi chiedere l'estratto prima che esista")

Questa è l'unica regola di sequenza, e saltarla produce un ciclo che non termina mai.

**Chiedere `type="wasm"` è la sola cosa che costruisce l'estratto wasm di un dataset.** L'estratto è materializzato in modo lazy, da quella richiesta — o dal percorso di aggiornamento dopo che un dataset diventa pronto — e da nessun'altra parte.

Quindi se [l'espulsione](/it/guides/eject-your-dashboard.md) riporta `dataLane.id == "token"` con la ragione `no-wasm-extract`, la soluzione è eseguire **una query per ogni dataset nominato** con `type="wasm"` — va bene qualsiasi `SELECT` banale sull'alias — e poi espellere di nuovo. Un piano restituito significa che l'estratto ora esiste; per questo scopo puoi ignorare completamente il contenuto del piano.

**Pubblicare non cuoce un estratto.** "Ripubblica e riespelli" girerà all'infinito.

## La corsia anonima, e perché non è nel riferimento API[​](#la-corsia-anonima-e-perché-non-è-nel-riferimento-api "Link diretto a La corsia anonima, e perché non è nel riferimento API")

La corsia sopra ha bisogno di una chiave API, perché sei tu che costruisci una dashboard. Quello che chiama il browser di un *visitatore* è un endpoint diverso e non autenticato: il **piano wasm** della viz, a

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

È deliberatamente **assente dal riferimento API generato e da entrambi gli SDK**, e quell'assenza è una decisione di progetto piuttosto che un'omissione. La route è anonima e rivolta al browser; non c'è alcuna chiamata con chiave API per essa, quindi non ha posto in un SDK a chiave API. È esclusa a livello di framework, motivo per cui non la troverai sfogliando [il riferimento](/it/reference/vibedasher-sdk-api.md) — la sola descrizione che esiste è questa pagina e il runner che la chiama.

Le sue protezioni sono **identiche** a quelle della route di cache anonima che le sta accanto: l'account deve esistere e permettere dashboard pubbliche, e la viz deve essere pubblica **e** pubblicata. Una dashboard privata o protetta risponde 404 qui. Nulla di tutto ciò è un default lato client che puoi sovrascrivere.

Restituisce `{ sources: [...], unavailable: [...] }`. Un dataset non abilitato a wasm è riportato in **`unavailable` con una ragione** invece di essere tolto da `sources` in silenzio — perché un client che ricevesse un elenco `sources` corto e nessuna spiegazione renderebbe una dashboard parziale e la considererebbe finita.

### Perché una dashboard protetta non potrà mai essere servita così[​](#perché-una-dashboard-protetta-non-potrà-mai-essere-servita-così "Link diretto a Perché una dashboard protetta non potrà mai essere servita così")

Le righe di una dashboard protetta sono delimitate per visualizzatore riscrivendo l'SQL lato server. Un file Parquet prefirmato è **l'intera tabella**. Non c'è modo di onorare un predicato di riga che il client potrebbe semplicemente rifiutare di eseguire — quindi per le dashboard protette la corsia non è "non ancora implementata"; è strutturalmente esclusa, e lo sarà sempre.

## A cosa stai rinunciando[​](#a-cosa-stai-rinunciando "Link diretto a A cosa stai rinunciando")

Dichiaralo prima di costruire, perché è la metà che non puoi cambiare dopo.

**L'estratto ha come chiave il dataset e lo snapshot, non la tua query.** Lo stesso `SELECT` con e senza `WHERE` restituisce lo stesso file identico. Il tuo predicato non lo rimpicciolisce. Conseguenze, tutte la stessa conseguenza:

* Contiene **tutte le righe** di ogni dataset che la dashboard legge.
* È **un unico file, lo stesso file**, per tutti quelli che aprono la pagina.
* La pagina non emette alcuna query al server, quindi **nulla nel percorso potrebbe dare a un visualizzatore meno righe che a un altro**.
* Un filtro applicato nel browser cambia ciò che viene **disegnato**, mai ciò che è stato **consegnato** — e i byte sono nel tab di rete in ogni caso.
* Serve lo **snapshot pubblicato**, non una query dal vivo. Aggiornare ciò che mostra significa ripubblicare.

In cambio: nessuna chiave da far trapelare, nessun token da rinnovare, nessun backend tuo da far girare o pagare, nessun rate limit davanti alla pagina, e una dashboard che continua a renderizzare da un host statico.

**Vale anche la pena sapere: il limite sulle righe non si applica qui.** La corsia lato server si ferma a 100.000 righe e imposta `truncated`, che rende silenziosamente sbagliato ogni totale calcolato sul prefisso. Il Parquet della corsia wasm è l'intero dataset per snapshot, proprio perché ha come chiave il dataset invece del tuo risultato — quindi è anche la risposta onesta a un insieme di risultati davvero grande, non solo a un problema di credenziali.

## Quando scegliere qualcos'altro[​](#quando-scegliere-qualcosaltro "Link diretto a Quando scegliere qualcos'altro")

| Ti serve                                                 | Corsia                                                                                                   |
| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| righe che leggerai e su cui ragionerai                   | `run_query` / `client.query` — la corsia backend di default                                              |
| molte righe su disco                                     | `run_query_to_file(output_path=...)`                                                                     |
| una dashboard pubblica senza credenziali e senza backend | **wasm**                                                                                                 |
| che visualizzatori diversi vedano righe diverse          | un **token di incorporazione con scope** che porta `filters`, o codice server fidato che restringe l'SQL |
| dati dal vivo invece di uno snapshot pubblicato          | la corsia backend, dietro un server che controlli tu                                                     |

Un client con token con scope **rifiuta** `type="wasm"` senza mezzi termini invece di girare in silenzio lato server: un chiamante che ha chiesto wasm e ha ottenuto righe comunque non avrebbe modo di accorgersene. Quel rifiuto segue lo stesso principio di `fallbackReason` — un cambio di corsia non deve mai essere invisibile.

Per le due configurazioni che *possono* restringere un visualizzatore, vedi [Filtraggio a livello di riga](/it/guides/who-can-see-what.md#row-level-filtering-the-embed-lane-and-only-the-embed-lane).
