How it's built
Who built it, why, with what, and how it is tested.
Who built it
I'm Ahmed Malik, a computer science graduate from UET Lahore. PulseMetrics is the fifth project in a ten-project portfolio I am building to work as a developer, a business analyst or a project manager.
I built it end to end: the tracking snippet, the API, the database rules, the dashboard and the tests.
The story
Small apps can't afford a tool like Mixpanel, so their builders never learn where people drop off. I wanted to see how product analytics works by building the whole thing: a snippet that sends events, an API that receives them, a database that keeps them, and charts that answer two questions, where do people leave and did the new version really win.
The two apps I wanted to measure first, QueueCare and CommitteeKeeper, put private links in their page addresses: a patient's status link, a member's payment link. An analytics tool that records page addresses would have copied those links into a second database. So the first rule I wrote (P-1) was that PulseMetrics never stores an address, a token, a name, a phone number or an email, only a fixed event name and a random browser id, and I made the server and the database refuse anything else.
I also chose to learn how to build a real backend on my own: a standalone Express API on Render with Neon Postgres, rather than keeping everything inside the dashboard. Render's free plan puts the API to sleep when nobody uses it, so the snippet is built to give up quickly and retry later instead of ever slowing down the app it sits in.
The problem
Small apps can't afford product analytics, so their builders don't know where users drop off, and the free alternatives either collect personal data or show numbers without saying how far to trust them.
The technology
Dashboard
- Next.js 16
- React 19
- TypeScript
- Vercel
API
- Express 5
- TypeScript
- zod
- Better Auth
- Drizzle ORM
- Render
Database
- Postgres on Neon
- row-level security
- numbered SQL migrations
Tests
- Vitest
- Playwright
- axe-core
- embedded Postgres
Architecture
A snippet in someone's app sends a fixed event name and a random browser id to the API. The API checks it and inserts it through one database function. The dashboard asks the same API for reports, and the database only ever shows an owner their own rows.
Decisions worth knowing
- Nothing private can get in. The event is exactly three fields. The server refuses any other field, the event name must match a pattern the database enforces too, and tests prove no address, token, name, phone or email is ever stored.
- Owners only see their own data. Every dashboard request tells the database who is signed in, and row-level security hides everyone else's rows. The public tracking route runs as a separate database role that can run one function and nothing else.
- Honest statistics. The A/B calculator never says a version won unless p is under 0.05 and the sample is big enough, and it explains that the p-value is not the chance that a version is better. The funnel warns when fewer than 30 people started.
- Built for free hosting that sleeps. The API sleeps after 15 quiet minutes. A free pinger keeps it awake from 9 am to 6 pm Pakistan time, the dashboard shows a waking-up state, and the snippet times out after 5 seconds and retries the event names on the next page view.
- Days follow Pakistan time. Daily counts split at midnight in Pakistan (UTC+5), and a test pins the exact boundary at 19:00 UTC.
Tests
Honest limits
- No one outside the author has used it yet, so any usage numbers quoted anywhere come from test traffic or the author's own apps and are labelled that way.
- Safari and Firefox are not tested; the browser tests run in Chrome, Edge and a 375 px phone view.
- The free hosting plans sleep when idle, so the first visit after a quiet spell can take about a minute.
Links
Credits
Built with open-source libraries: Next.js and React, Express, Better Auth, Drizzle ORM, zod, node-postgres (pg), express-rate-limit, Playwright and axe-core, Vitest, embedded-postgres, esbuild and tsup.
Fonts, all under the SIL Open Font License: Barlow Condensed (Jeremy Tribby), Hanken Grotesk (Hanken Design Co.), Red Hat Mono (Red Hat).
Privacy · No cookies on your visitors. No page addresses stored.