DEV Community

Soumyadip Maity
Soumyadip Maity

Posted on

I Launched My Analytics Tool 3 Months Ago. It Got 1 Upvote. Here's What I Rebuilt.

The Original Problem (Still Valid)

Google Analytics sends every user interaction to Google's servers. Every click, every session, every IP address — all of it leaves your infrastructure.

For developers who care about user trust, that's a problem.

The alternatives were either expensive (Mixpanel at $28/month), limited (Plausible gives you pageviews but no custom events or error tracking), or painful to self-host (Matomo).

So I built my own. The idea was solid. The execution needed work.


What Was Wrong With v1

Problem 1: API response time was 3-4 seconds

Every analytics event was being written to MongoDB synchronously, inside the request handler. Under any real load, this meant:

  • The /log endpoint was slow
  • Events were being dropped
  • The tracked application felt the impact

This was unacceptable for an analytics tool. Analytics should be invisible.

Problem 2: Wrong database schema for time-series data

I was storing analytics events as regular MongoDB documents. That works, but it's not optimized for what analytics actually is: time-ordered data queried by date range.

Queries like "show me the last 7 days" were doing full collection scans.

Problem 3: No separation of concerns

User data, analytics events, settings, API keys — all mixed together in MongoDB. No relational integrity, no proper foreign keys, no ACID transactions where they were needed.


What I Rebuilt

Fix 1: MongoDB Time-Series Collections

MongoDB has a purpose-built collection type for time-ordered data. I migrated all analytics events to Time-Series collections.

The result:

  • Automatic data bucketing by time — range queries are now index-native
  • Columnar compression — storage costs dropped significantly
  • Built-in TTL — data automatically expires after 6 months, no manual cleanup
  • API response: 3-4 seconds → 400ms

That's not a typo. The same query that took 3-4 seconds now runs in under 400ms.

Fix 2: Async Processing With BullMQ + Redis

The /log endpoint no longer writes to MongoDB directly. Every incoming event goes into a BullMQ job queue backed by Redis. A background worker processes the queue and writes to MongoDB asynchronously.

Client SDK
   │
   ▼
Express API  ──→  BullMQ Queue (Redis)
                       │
                       ▼
                  Worker Process
                       │
                       ▼
             MongoDB Time-Series
Enter fullscreen mode Exit fullscreen mode

The API endpoint now just validates the request and pushes to the queue — it responds in milliseconds. Events are processed in the background. Your application is never affected.

Fix 3: Polyglot Persistence

Analytics events belong in MongoDB Time-Series. But user accounts, project settings, API keys, and billing records are relational data — they have foreign key relationships and need ACID transactions.

I added PostgreSQL via Supabase for all relational data. Supabase also handles Row Level Security.

Now the data layer is:

Data Type Database
Analytics events MongoDB Time-Series
Users, projects, settings PostgreSQL (Supabase)
Queue, caching, presence Redis

Each database does what it's best at. This is called polyglot persistence — and it's the right pattern for this kind of workload.

Fix 4: Real Infrastructure

v1 was running on a basic setup. v2 runs on:

  • Microsoft Azure VPS — backend server
  • Cloudflare CDN — global edge delivery, DDoS protection
  • PM2 — process management, zero-downtime restarts
  • Nginx — reverse proxy

New Features in v2

Beyond performance, I added features that were missing:

  • Scroll depth tracking — how far users actually scroll (Pro)
  • Core Web Vitals — LCP, FID, CLS from real users (Pro)
  • Domain ownership verification — DNS TXT record or meta tag, so you can only track sites you own
  • On-demand data deletion — delete individual events, date ranges, or everything from the dashboard
  • Free forever plan — no credit card, no expiry

The Current Stack

Layer Technology
SDK TypeScript, Beacon API, ~7.4KB gzipped
Frontend Dashboard Next.js, React, TypeScript, Tailwind CSS, Shadcn UI
Backend Node.js, Express, TypeScript
Analytics DB MongoDB Time-Series
Relational DB PostgreSQL via Supabase
Queue BullMQ + Redis
Auth Supabase Auth (OTP)
Infrastructure Microsoft Azure + Cloudflare
SDK Distribution npm + jsDelivr CDN

What the Dashboard Looks Like Now

The dashboard gives you:

  • Page views, sessions, bounce rate
  • Custom events with properties
  • JavaScript errors with stack traces
  • Referrers, UTM params, device/browser breakdown
  • User location (country/city only — IP is discarded immediately after geolocation)
  • Scroll depth heatmap (Pro)
  • Core Web Vitals from real users (Pro)

All without a single cookie. No consent banner required.


The Privacy Architecture (Unchanged, Still Core)

This didn't change between v1 and v2 because it was right from the start:

  • No IP storage — IPs are used for geolocation then immediately discarded
  • No cookies — session identity uses a daily-rotated hash, no persistent tracking
  • No cross-site tracking — your data stays in your project
  • No data selling — ever
  • GDPR, CCPA compliant — no consent banner needed because no tracking cookies

What I Learned From 1 Upvote

The product wasn't the problem. The launch was.

I posted on Product Hunt with a brand new account, no audience, no prior community engagement. The algorithm doesn't surface products from cold accounts. That's not a conspiracy — it's just how it works.

The lesson: build an audience before you need it.

I'm doing that now. Writing here, sharing on Twitter, building in public. If you're reading this, you're part of that process.


Try It

npm install ucoder-insight
Enter fullscreen mode Exit fullscreen mode
import { initUcoderInsight } from 'ucoder-insight';

initUcoderInsight("YOUR_PROJECT_ID");
Enter fullscreen mode Exit fullscreen mode

That's the entire setup. It auto-tracks page views, SPA navigation, JavaScript errors, and Core Web Vitals from that single line.


If you're building something and tired of sending your users' data to Google — give it a try.

And if you have feedback, I genuinely want to hear it. Drop a comment.


Built by Soumyadip Maity — Full Stack Developer, final year CS student, West Bengal, India.
Previously: I Was Tired of Sending My Users' Data to Google — So I Built My Own Analytics

Top comments (0)