You open your laptop and find a 200 MB Parquet file waiting in your inbox. A teammate says “spot-check the schema and make sure the new revenue column looks right.” The traditional playbook demands spinning up a local PostgreSQL instance, configuring the DuckDB CLI, or uploading sensitive payloads to a cloud warehouse just to peek inside a file. That overhead eats up valuable development time, and the cloud route introduces unnecessary data exposure risk for a simple sanity check. Now there is a direct alternative. Open a browser tab, drop the file, and write a SQL query. The engine is DuckDB running entirely through WebAssembly, and every byte stays on your machine from start to finish.
CapyToolkit’s SQL Data Workbench turns any browser tab into an analytical query engine. Once the engine loads, it reads 11 file formats natively (Parquet, Arrow, Feather, CSV, TSV, JSON, NDJSON, Avro, DBF, Excel, and SQLite) and gives you DuckDB’s full SQL dialect against the data. No account, no daemon, no socket, no file leaving your device. This post walks through why this approach exists, how the engine under the hood actually works, what you can import and export, and the query patterns that make this tool worth bookmarking.
Why Inspect Data Files Without a Server
The gap between “I have a data file” and “I can run SQL against it” is wider than it should be. You can open a CSV in a text editor, you can double-click a SQLite file and browse tables in a GUI, but the moment you want to aggregate across a wide Parquet export or JOIN two sheets inside an Excel workbook you need a query engine. That usually means installing software or pushing data through a third-party service.
Local database installs like PostgreSQL or the DuckDB CLI work, but you need to install them, figure out the import command, and remember the export flags. Cloud query services like BigQuery or Snowflake ask you to upload the file first. For a one-off sanity check on a pipeline output, that overhead dwarfs the actual work. Worse, those files routinely contain personal data, internal access tokens, or revenue figures, making a third-party server upload an unnecessary liability for a routine, five-minute data validation check.
Browser-based SQL closes that gap entirely. You open a tab, drop a file, write a query, and download the result. Nothing leaves your machine because there is no server to send it to. If you work with sensitive data (healthcare records under HIPAA, payment data under PCI-DSS, or legal documents under privilege), the ability to prove that your query ran locally matters as much as the query itself. Before you even open a file, you can run it through a PII scrubbing step to detect and tokenize personal fields, secrets, or internal identifiers so that downstream sharing stays safe. CapyToolkit’s workbench processes everything inside the browser tab, and you can verify that claim by pulling your network cable mid-session and watching every query still execute.
How DuckDB-WASM Powers In-Browser Queries
Running real SQL with no server anywhere takes a specific engine. DuckDB is that engine: a columnar analytical database built for scan-heavy queries rather than transactional workloads.1 The workbench embeds DuckDB compiled to WebAssembly, a binary format that runs sandboxed inside any modern browser at near-native speed.23 The compressed engine binary downloads once per session from CapyToolkit’s own CDN at a highly optimized footprint, and stays cached between visits so repeat loads are instant.
Engine Architecture
By granting the engine direct memory access without crossing a JavaScript boundary for every operation, WebAssembly handles million-row Parquet files through intensive column scans inside optimized, low-overhead execution loops. Running every query inside a Web Worker keeps the main thread free, so aggregations on a 500 MB CSV never freeze the UI while you type the next query.4
There is no daemon to start, no port to bind, no socket to connect to. The engine lives inside the page, and when you close the tab it is gone. That is the architecture: one library, one worker, one sandbox, and a session that has zero persistent state on any server.
Offline Operation
After the initial engine download, the workbench works with no network connection at all. By processing every operation locally, you can load a file, disconnect from the internet entirely, and run complex analytical queries with zero cloud dependencies. This matters in air-gapped environments where machines have no external connectivity, travel networks where minimizing data exposure is a priority, and sensitive network zones where even a DNS lookup to a third-party CDN qualifies as an audit finding. Because the engine loads from CapyToolkit’s CDN and gets cached by your browser, the one-time download covers every future session.
DuckDB SQL Dialect
Formats and loaders are plumbing. The SQL is where the actual work happens. DuckDB implements a superset of standard SQL, so nearly everything you write for PostgreSQL or BigQuery here too.5 Window functions like ROW_NUMBER(), RANK(), and LAG() need zero configuration. If you need to deduplicate rows by a grouping key, the QUALIFY clause filters window-function results without wrapping the whole thing in a subquery. For type conversions mid-query, the :: cast operator keeps expressions short.
A few features worth knowing upfront. PIVOT rotates a long table wide and UNPIVOT rotates it back. SELECT * EXCLUDE (col) projects every column except the ones you name, and SELECT * REPLACE (expr AS col) rewrites a column inline without listing every other column manually. The full DuckDB friendly-SQL reference catalogs every dialect extension, from list comprehensions to dot-operator struct access. If you work across multiple log-style files and need to inspect them without loading into the workbench, Big Log Explorer handles multi-gigabyte JSONL files in the browser through IndexedDB streaming.6
To see this friendly SQL dialect in action, consider how a single window-function expression handles row deduplication without the overhead of nested subqueries:
SELECT *
FROM import
QUALIFY ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY updated_at DESC) = 1
That returns the most recent row per user_id without a CTE or nested subquery. If DuckDB’s window-function documentation feels terse, read about how DuckDB works via WebAssembly in the browser for a deeper look at what makes these queries possible without a server.
Import Formats and Loader Paths
The workbench accepts 11 file formats, and where a file lands depends on which loader handles it. DuckDB reads columnar and text formats directly, a JavaScript bridge decodes the formats DuckDB cannot open on its own, and SQLite files get a dedicated reader. Once loaded, every format looks the same to your SQL: a table with browsable columns and a schema you can inspect immediately.
Format Support
To bridge the gap between varying data serialization styles, the workbench automatically triages incoming uploads through specific, optimized loader subsystems:
| Format | Example Extension | Loader Path | Notes |
|---|---|---|---|
Parquet / GeoParquet | .parquet | DuckDB native | Column pruning, filter pushdown7 |
Arrow / Feather | .arrow, .feather | DuckDB native | Becomes a view named after the file |
CSV / TSV | .csv, .tsv | DuckDB native | Auto type and delimiter detection |
JSON / NDJSON | .json, .jsonl, .ndjson | DuckDB native | Single reader covers both formats |
Avro | .avro | JavaScript bridge | Schema from container header sets types |
DBF | .dbf | JavaScript bridge | Legacy GIS and shapefile companion format |
Excel | .xlsx | JavaScript bridge | Each sheet becomes a separate table |
SQLite | .db, .sqlite | sql.js | Processed via a specialized browser-side reader runtime for lightweight, legacy database compatibility8 |
Schema Discovery
The moment a file finishes loading, the schema panel lists every table and column type. For Excel workbooks, each sheet becomes a table named after the sheet, with headers taken from row one. For SQLite databases, every internal table appears at once. For Parquet and Arrow files, column types map directly to DuckDB’s type system. A preview query runs automatically so you see real rows (not just column names) before you type anything.
Multi-Table Queries
While the browser-sandbox environment restricts the engine to one file per session, multi-table JOINs work across every sheet in an Excel workbook or every table in a SQLite database without any extra setup. One file per session is the current constraint. If you need to combine data from two separate files, export the results from one query, re-import alongside the other file, and run the cross-file logic from there.
Exporting Results
The Export button offers four output formats:
- CSV: standard comma-separated file, built from the result set on screen
- JSON: array of objects, one entry per row, instant download
- Excel: single-sheet
.xlsxworkbook from the current results - Parquet: runs
COPY ... TOthroughDuckDBso the file matches exactly what the SQL engine produced; available forDuckDB-loaded formats but unavailable duringSQLitesessions, which use a separate browser-side reader runtime for lightweight database compatibility
If you need to verify that an export matches the original byte-for-byte, compute a SHA-256 checksum locally to compare the digest without leaving your browser.
When to Use Client-Side SQL Over Traditional Tools
Not every data task needs a browser tab. Client-side SQL makes the most sense for the 80% of data work that is exploratory rather than operational. Quick pipeline validations fall into this category: you want to confirm that the nightly ETL job actually populated the new nullable column, or spot-check for null rates before handing the file to a teammate. Installing database software for a five-minute inspection is a bad time trade.
Sensitive data workflows are the other major use case. Healthcare, finance, and legal teams often handle files that cannot leave the machine by policy or regulation. A browser-based workbench gives you real analytical SQL without a file touching a server, and that audit trail matters when a compliance review asks how you inspected that data. For recurring validation checks, pair the workbench with a cron-expression parser to schedule and document a regular data quality routine in plain English. Alternatively, use a SQL-native generate_series to build a calendar table and window your checks by date.
Client-side SQL is not a replacement for a production data warehouse. It is for the in-between tasks: the ad hoc question, the file landed on your desk, the query you need to answer before your next meeting. Deploying an isolated, client-side workbench effectively detaches your everyday data investigations from network availability, host infrastructure, or corporate cloud logging footprints entirely. CapyToolkit’s browser-based utilities include this workbench alongside dozens of other privacy-first developer tools, all running locally with no uploads.
Practical Query Patterns for Local Data Inspection
Transitioning from basic file loading to active discovery requires shifting toward high-efficiency exploration strategies. Before composing multi-line analytical queries, spend sixty seconds mapping out the raw data. Run SELECT * LIMIT 5 to see the shape and column names. Follow it with information_schema.columns to list every column and its inferred type: SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'import'. You now have a quick type map before doing real work.
Profiling numeric columns in a single pass becomes trivial when combining aggregations like COUNT(*), AVG(price::DOUBLE), and MAX into one diagnostic statement. Adding COUNT(DISTINCT price) reveals cardinality, which tells you whether a numeric column is really an identifier wearing a number’s clothes.
Finding duplicates follows a reliable pattern: a GROUP BY with HAVING COUNT(*) > 1 surfaces every key that repeats. From there, the QUALIFY clause isolates exactly which row to keep without wrapping logic inside a nested CTE. When the retention rule changes (highest amount instead of earliest timestamp, for example), swapping the ORDER BY inside the window function adjusts the output instantly.
Type exploration saves you from silent errors. DuckDB auto-detects types on load, but a column that looks numeric might arrive as a string if a single row contains a dollar sign or a comma. The :: operator fixes this mid-query: REPLACE(price, '$', '')::DOUBLE strips the currency symbol and converts to a float in one expression. Use ::DATE on timestamp strings like created_at::DATE to compare calendar dates without time-of-day noise.
For string-heavy files, regexp_extract pulls structured values out of free-text columns. split_part breaks comma-separated lists into individual elements. UNNEST flattens array or list columns into one row per element, which is useful when a Parquet file stores tags or categories as an ARRAY<VARCHAR>. If your test files contain JWT tokens in a payload column and you need to understand the claims structure, a JWT decoder inspects token headers and payloads without the tokens ever leaving your machine. That is a surprisingly common scenario when debugging auth flows against stored Parquet or JSONL debug logs. For a deeper look at the Parquet format itself (column pruning, filter pushdown, and how DuckDB reads it), the Parquet format guide for browser SQL users covers the format internals alongside workbench-specific tips.
Window functions go beyond deduplication. LAG(col, 1) fetches the previous row’s value within a partition, which lets you compute session time gaps or detect sequential anomalies. SUM(amount::DOUBLE) OVER (ORDER BY created_at ROWS UNBOUNDED PRECEDING) builds a running total without a self-join. These patterns replace entire post-processing scripts with a single query, and they run at full speed inside the browser engine.
First Query in Under a Minute
The first time through takes about sixty seconds. Opening the browser-based SQL workbench that runs DuckDB queries on local files without any upload triggers the DuckDB-WASM engine download in the background at a highly optimized compressed footprint, and your browser caches it for every future visit so repeat loads feel instant. Dropping any supported file onto the drop zone (or clicking to browse) populates the schema panel immediately: column names, types, and a preview query run on their own so you see real data before typing anything.
Writing a basic query like SELECT * FROM import LIMIT 10 and clicking Run instantly populates the results grid, enabling you to dynamically sort columns, scroll through rows, and resize fields to inspect wide text locally. From there, exporting the full result set as CSV, JSON, Excel, or Parquet takes a single click, and copying to clipboard works when the next stop is a spreadsheet or a Slack message.
There is no account to create, no installation to manage, and no network needed after that first engine download. Bookmark the page and tomorrow the entire pipeline opens in an eager tab, engine cached, schema panel ready, waiting for a file. If you want to go deeper after the first look, start exploring the full collection. Every tool runs locally with zero uploads.
- 1.
DuckDB, “duckdb/duckdb,” github.com, accessed June 2026. https://github.com/duckdb/duckdb/blob/main/README.md
- 2.
DuckDB, “DuckDB Wasm,” duckdb.org, accessed June 2026. https://duckdb.org/docs/current/clients/wasm/overview
- 3.
World Wide Web Consortium, “WebAssembly Core Specification,” w3.org, May 2026. https://www.w3.org/TR/wasm-core/
- 4.
Mozilla Developer Network, “Web Workers API,” developer.mozilla.org, April 2025. https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
- 5.
DuckDB, “DuckDB’s SQL Dialect,” duckdb.org, accessed June 2026. https://duckdb.org/docs/1.3/sql/dialect/overview
- 6.
Mozilla Developer Network, “IndexedDB API,” developer.mozilla.org, April 2025. https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
- 7.
Apache Software Foundation, “Implementation status,” parquet.apache.org, February 2026. https://parquet.apache.org/docs/file-format/implementationstatus/
- 8.
sql.js Contributors, “sql.js,” github.com, accessed June 2026. https://github.com/sql-js/sql.js