Client-side SQL notebook powered by DuckDB-WASM
I Hate Excel, so I built a simple, client-side SQL job runner. 📊
📥 Load, 🔄 transform, and 📈 visualize your data directly in the browser—no setup, no fuss. 🚀
- 🌐 Zero Setup: Pure client-side execution—no server, no installation
- 📂 Multi-Source Support: Load multiple CSV, Parquet, Excel files simultaneously
- 📓 Notebook Interface: Organize your analysis in executable cells (Markdown, SQL, Charts, Tables)
- 📊 Plotly Visualizations: Create interactive charts with full Plotly.js support
- 💾 Portable Exports: Export standalone HTML files with embedded data
- 🔄 Auto-execution: Configure cells to run automatically after data loads
- 🎨 Dark Mode UI: Modern, responsive interface
- 📄 PDF Export: Print-friendly output for reports
This project started on December 19, 2025 and is far from finalized.
- 🚧 Not production-ready - Active development, APIs may change
- 🐛 Expect bugs - Testing and stabilization ongoing
- 📝 Documentation incomplete - Features being added daily
- 💡 Feedback welcome - Open issues/PRs to help shape the project
Current status: Experimental / Proof of concept
Use at your own risk - Not recommended for critical workflows yet
- Download
index.html - Open it in your browser
- Drag & drop your CSV/Excel file
- Start querying with SQL!
git clone https://github.com/ihatexcel/sqljob.git
cd sqljob
# Open index.html in your browser - that's it!| Scenario | Solution |
|---|---|
| 📊 Replace Excel Data Pipelines | Distribute SQL-based data processing without requiring end-users to install anything |
| 🔄 Power Query Alternative | Share transformation logic as portable HTML—no Excel license needed |
| 📤 Data Products for Non-Technical Users | Package your SQL workflows into self-service tools colleagues can run in their browser |
| 🎓 SQL Training Without Setup | Give students/analysts a zero-install environment to learn data manipulation |
| 📈 Quick Data QA/Validation | Drop files, run checks, export results—2 minutes from raw data to insights |
| Type | Description | Use Case |
|---|---|---|
| 📝 Markdown | Rich text, HTML, SVG | Documentation, headers |
| 📂 Sources | File upload zones | Load CSV/Excel/Parquet |
| 🗄️ SQL | Execute queries | Data exploration |
| 📊 Table | Display results | Preview datasets |
| 📈 Plot | Plotly charts | Visualizations |
| 📤 SQL Export | Download results | Export transformed data |
| 🖼️ Iframe | Render HTML | Custom reports |
Core:
- DuckDB-WASM - In-browser SQL engine
- Alpine.js- Reactive UI framework
UI Components:
File Processing:
- SheetJS (xlsx)
0.18.5- Excel parsing
This project draws inspiration from:
- SQLrooms - Production-ready data webapp leveraging DuckDB-WASM
- Perspective.js - Streaming data visualization (note: Plotly.js used here requires intermediate arrays, not streaming-capable)
- Huey - Vanilla JS approach to DuckDB-WASM (no framework overhead)
- Power Query (Excel) - ETL for the masses
Special thanks to these projects for pioneering accessible data tools!
This project contains code generated and refined with:
Human-written architecture, AI-assisted implementation. 🤝
sqlJob uses a JSON configuration format for cells:
{
"job": {
"autoExecuteWithoutSources": false,
"cells": [
{
"type": "markdown",
"content": "# sqlJob ⚡💻\n## I Hate Excel, so I built a simple, client-side SQL job runner. 🛠️\n📥 Load, 🔄 transform, and 📊 visualize your data directly in the browser—no setup, no fuss. 🚀"
},
{
"type": "sources",
"autoRunNextCells": true,
"sources": [
{
"name": "source1",
"importText": "Glissez-déposez votre fichier ici",
"query": "CREATE OR REPLACE TABLE source1 AS SELECT * FROM read_csv_auto('{fileNameUpload}', ALL_VARCHAR=true, HEADER=true)",
"xlsx": {
"options": {
"type": "array",
"raw": false,
"dateNF": "dd/mm/yyyy",
"cellDates": true,
"cellNF": false,
"cellText": false
},
"toCsvOptions": {
"dateNF": "dd/mm/yyyy",
"FS": ",",
"RS": "\n"
},
"sheetSelection": {
"type": {
"auto": true,
"index": false,
"name": false
},
"index": 0,
"name": ""
}
}
}
]
},
{
"type": "table",
"query": "SELECT * FROM source1 LIMIT 100",
"maxRows": 1000
},
{
"type": "sqlExport",
"query": "COPY (SELECT * from source1) TO '{fileName}' (FORMAT CSV, HEADER, DELIMITER ';')",
"fileNameQuery": "SELECT 'export_' || current_timestamp::text || '.csv' as file_name",
"mimeType": ""
},
{
"type": "plot",
"query": "// Variables: container, Plotly, + les tables configurées\nconst limitedSource = source1.slice(0, 1000);\nconst x = limitedSource.map(r => Object.values(r)[0]);\nconst y = limitedSource.map(r => Object.values(r)[1]);\n\nPlotly.newPlot(container, [{\n x: x,\n y: y,\n type: \"bar\"\n}], { title: \"Graphique\" });",
"tables": "source1"
}
]
},
"ui": {
"devMode": true
}
}- JSON Config: Portable configuration file
- Base64 Config: Embed in file
- Standalone HTML: Fully self-contained with embedded data
- PDF: Print-ready reports via browser print
sqljob/
├── index.html # Main notebook interface
├── README.md
├── LICENSE
ConfigManager // Configuration handling
FileHandler // File I/O & compression
DuckDBManager // Database operations
PlotlyManager // Chart generationRequirements:
- Modern browser with WASM support
CompressionStreamAPI (for exports)
This project is licensed under the MIT License - see the LICENSE file for details.
- DuckDB Team - For the incredible WASM build
- Alpine.js Community - For the reactive simplicity
- Plotly Team - For open-source charting
- Open Source Community - For the tools that made this possible
Théo Nobella-Pichonnier
If this project helped you, consider giving it a star! ⭐
Made with ❤️ by Théo Nobella-Pichonnier "I hate Excel, so I built this."