Inspiration

I'd already built a key-value store and a container runtime, so I wanted to see the other side of the stack: a product you'd actually point a browser at. Most "simple" analytics tools still end up as a pile of managed services — a hosted database, a queue, a chart SaaS. I wanted to see how far a single process and a single file could actually get.

What it does

DevPulse is a self-hosted web analytics platform that runs as one Node process backed by a single SQLite file (via better-sqlite3) — no separate database server to operate. A ~2kB typed browser SDK sends events to a batched POST /ingest endpoint, with a sendBeacon-based pixel fallback for browsers that block the request on page unload, and the endpoint reports partial success (202/207) when some events in a batch are accepted and others aren't. The aggregation engine computes visitors, events, sessions, bounce rate, and session length bucketed by hour, day, week, or month, plus breakdowns by page, referrer, country, device, browser, and OS. It also supports ordered, windowed funnels and cohort retention triangles, where a cohort is defined by a visitor's first-ever visit date. Bot traffic is still stored, but excluded from the default metrics rather than silently dropped. The whole thing is TypeScript end to end, organized as a monorepo (shared/sdk/api/web packages), and the React dashboard draws its own charts with hand-rolled SVG — no charting library, no CSS framework.

How I built it

The operational pieces mattered as much as the analytics logic: write keys are hashed and rotatable, auth uses JWTs with rotating refresh tokens, access is scoped with role-based access control, each project gets its own rate limit, old data is cleared out by scheduled retention sweeps, and there's CSV/NDJSON export plus a Prometheus /metrics endpoint and a /health check — the things you'd actually need to run this somewhere and trust it. It's MIT licensed.

Challenges I ran into

Getting funnels and cohort retention right on a single SQLite file, without a dedicated OLAP engine doing the heavy lifting, meant being deliberate about indexing and query shape — it's easy to write a funnel query that's correct but falls over once there's real traffic volume. The ingestion partial-success reporting (202/207) was also trickier than it sounds: a batch endpoint that just returns 200 or 500 hides exactly the failures a self-hosted operator needs visibility into.

Links

Built With

Share this project:

Updates