on GitHub

Isomorphic SQL

The same SQL runs wherever it makes sense to run. By default every query below executes in your browser (DuckDB-WASM), but nothing about the SQL changes when you push it to the server: same tables, same names, same results.

The db catalog

db is a catalog that holds all the data this page exposes. It's named after the db = Database() you create in data.py. This page's data.py publishes one table, large_data, so its fully-qualified name is db.main.large_data: main is the default schema, so you can just write db.large_data.

SQL

Querying a million rows

large_data has 1,000,000 rows (generated in data.py). Counting them runs entirely in the browser:

SQL

Predicate pushdown

Filters and column projections are pushed down into Python, so only the rows and columns you actually ask for are loaded into memory, never the whole table:

SQL

Running on the server

Wrap a query in db.exec(...) to run it on the server and get back only the result, handy when you'd rather not pull the data into the browser at all.

Safe to run untrusted SQL

Server-side SQL is usually where injection risk lives, but db.exec runs on a locked-down, per-session DuckDB connection: external access is off (no files, network, or other databases) and it's loaded only with the tables this page published. So even fully attacker-controlled SQL can reach nothing but this page's own data, never other pages, credentials, or the host.

SQL

From a local DuckDB

The browser isn't special. A page and its data share one origin, so these tables are reachable from any TYPE n6k client. Point a local DuckDB at this page: install the n6k extension, then ATTACH. The command below already targets the host you're reading this on.

-- launch the CLI as:  duckdb -unsigned
INSTALL n6k FROM 'https://storage.googleapis.com/n6k-duckdb-release';
LOAD n6k;

ATTACH 'n6k://<host:port>/sql/db' AS sql (TYPE n6k);
SELECT count(*) FROM sql.large_data;

sql is just the alias you picked on ATTACH. The server adopts it as the catalog name, so sql.large_data here is the same table the browser calls db.large_data. Same SQL, same data, wherever you run it.