The Problem
Teams need a quick way to turn raw data into shareable dashboards without building custom BI pipelines. Shaper addresses this by letting analysts write SQL that instantly renders charts, tables, and reports, while keeping data inside the user’s own DuckDB instance. The pain point it solves is the gap between ad‑hoc SQL exploration and production‑ready, embedded analytics that can be white‑labeled and audited.
What This Does
Shaper is a portfolio of four semi‑independent projects — ui (106 files, React + Tailwind), server (76 files, Go + DuckDB), pip-package (9 files, Python CLI), and npm-package (7 files, Node wrapper). The core data‑flow is: a user writes SQL in the UI, the Go server forwards the query to DuckDB via server/core/app.go:Init → server/core/app.go:GetDuckDB → db.Exec, then the results flow back through server/core/dashboard.go and are rendered by ui/src/lib/render.ts and ui/src/lib/utils.ts.
Key entry points and their reach:
- Init (
server/core/app.go:294) reaches 176 functions, itself called from one place. - Run (
main.go:516) reaches 187 functions, invoked from 15 places. - Start (
server/ingest/ingest.go:86) reaches 172 functions, called from 5 places.
The internal call graph shows a hub at ui/src/lib/utils (50 importers, 0 exports) and ui/src/lib/types (25 importers). Two import cycles exist in ui/src/lib/auth.ts and ui/src/lib/system.ts, meaning a change to either file can ripple through mutually reachable modules. Representative edges: getRenderInfo → findColumnByTag (46 calls), routes → RequirePermission (8 calls), collectVars → EscapeSQLString (5 calls).
How It Is Wired
Execution starts at main.go:139 (main) which boots the CLI, then delegates to main.go:516 (Run) which launches the HTTP server. The server’s Init creates a DuckDB connection (server/core/app.go:222) and spawns a secret‑generation routine (createDuckDBSecret). Query flow: routes → RequirePermission (permission check) → collectVars → EscapeSQLString → DuckDB exec → results piped back to ui/src/lib/render.ts for chart composition.
The most connected UI modules (ui/src/lib/utils, ui/src/lib/types) are imported by half the front‑end codebase; any refactor there has blast‑radius risk. The two circular imports (auth.ts, system.ts) should be broken by extracting shared types into a separate module or inverting one dependency. Oversized files (server/core/get_dashboard.go – 34 functions, 653 lines; ui/src/components/dashboard/index.tsx – 653 lines) and duplicated 6‑line blocks (576 repeats across 64 files) indicate opportunities to split responsibilities and extract helpers.
How To Use It
- Setup: The README provides a one‑command Docker launch:
docker run --rm -it -p5454:5454 taleshape/shaper
Then open http://localhost:5454/new in a browser. For local development, run go run main.go after ensuring Go 1.22+ and Node 20 are installed; the package.json and go.mod manage the respective runtimes.
- Configuration: No external config file is committed; environment variables are read inside
server/core/keys.goandserver/core/auth.go. The JWT secret is generated at runtime viacreateDuckDBSecret; setSHAPER_JWT_SECRETif you wish to override.
- Running it: After Docker start, use the UI at
http://localhost:5454/newto write SQL, or call the CLI viashaper(npm-package/index.js) to generate reports programmatically.
Real‑World Use
A product team wants a weekly “Sessions per Week” dashboard for internal stakeholders. They write the following SQL in the Shaper UI:
SELECT 'Sessions per Week'::LABEL;
SELECT
date_trunc('week', created_at)::XAXIS,
category::CATEGORY,
count()::BARCHART_STACKED,
FROM dataset
GROUP BY ALL ORDER BY ALL;
Shaper renders a stacked bar chart, allows embedding via the React SDK (npm-package/index.js), and can schedule PDF/PNG export through the server/snapshots/snapshots.go runner.
Code Health & Issues
Measured analysis (static, 177 files): 64 findings – 12 high, 44 medium, 8 low, 11 distinct kinds. Highlights:
- Hub module
ui/src/lib/utils(50 dependents) andui/src/lib/types(25 dependents) – high‑blast‑radius churn. - Import cycles in
ui/src/lib/auth.tsandui/src/lib/system.ts. - High cognitive‑load: deep nesting (max depth 6) in
ui/src/components/providers/MenuProvider.tsx,ui/src/components/TaskResults.tsx,ui/src/components/dashboard/index.tsx; branching density 64 over 118 lines inui/src/lib/render.ts. - Oversized files (
server/core/get_dashboard.go,ui/src/components/dashboard/index.tsx,server/ingest/ingest_test.go). - Duplicated code (576 repeated 6‑line blocks across 64 files, e.g.,
main.go,server/core/app.go).
SDLC observations from the code‑health audit (7 findings, 0 critical):
- Pin third‑party GitHub Actions to commit SHAs (
.github/workflows/ci.yml). - Pin container base image by digest (
Dockerfile: debian:13.4-slim). - Add dependency‑vulnerability gate on PR (no scan currently).
- Set
persist-credentials: falseon checkout step. - Add non‑root USER to the Docker image.
- Add
timeout-minutesto CI jobs lacking it. - Add convention files (
.editorconfig,.gitattributes, formatter config).
The Bottom Line
Shaper delivers a compelling SQL‑first path from data to embedded analytics, with a Go‑backed server and DuckDB at the core. The architecture is clear but contains several high‑impact maintainability concerns—circular imports, a heavily‑connected UI hub, and oversized/duplicated files—that should be tackled before scaling. It’s well‑suited for teams that want self‑hosted, SQL‑driven dashboards and are prepared to invest in refactoring the identified hot‑spots.