Notch‑Metrics‑style Menu Bar Analytics Tracker

Put your revenue and product analytics metrics in the notch

Welcome to another official UVF IT guide! In the next 45 minutes you’ll spin up a tiny menu‑bar app that records page‑views and revenue events, mimicking the core of Notch Metrics. We’ll use Node.js, Express, and SQLite – all free and runnable on your laptop.

The clone focuses on three things: a lightweight data model, a tiny HTTP API to receive events, and a simple HTML UI that could be embedded in a menu bar. Real Notch Metrics has dashboards, auth, and cloud storage – we’ll skip those for simplicity.

Before you start

Architecture Overview
LOCAL STACK reads/writes HTTP requests Node.js Express Server API + UI SQLite DB data.db file Browser UI http://localhost:3000
1

Phase 1: Scaffold the project

Create a fresh directory for the clone, initialize it as an npm project, and pull in the minimal libraries we need. This gives us a clean workspace and ensures all required modules are version‑locked.

We’ll use Express for the HTTP server and SQLite3 for a zero‑config embedded database – both run locally without extra services.

mkdir notch-metrics-clone
cd notch-metrics-clone
npm init -y
npm install express sqlite3
2

Phase 2: Define the data model

We need a tiny SQLite schema that can store two event types: page_view and revenue. Each row records when the event happened and a few key fields so we can later aggregate counts and totals.

The db.js module will create the data.db file if it doesn’t exist, run the schema creation statements, and expose a simple query helper for the rest of the app.

db.js

import sqlite3 from 'sqlite3';
import { open } from 'sqlite';

// Open (or create) the SQLite database file
export const db = await open({
  filename: './data.db',
  driver: sqlite3.Database
});

// Create tables if they don't exist yet
await db.exec(`
  CREATE TABLE IF NOT EXISTS page_view (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    url TEXT NOT NULL,
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
  );
  CREATE TABLE IF NOT EXISTS revenue (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    amount REAL NOT NULL,
    currency TEXT NOT NULL,
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
  );
`);

// Simple helper for inserting rows
export async function insert(table, data) {
  const cols = Object.keys(data).join(',');
  const placeholders = Object.keys(data).map(() => '?').join(',');
  const values = Object.values(data);
  await db.run(`INSERT INTO ${table} (${cols}) VALUES (${placeholders})`, values);
}
3

Phase 3: Build the API & UI

In this step we create a tiny Express server that can receive analytics events and display them in a simple HTML page. The /track endpoint accepts a JSON payload, stores it in memory, and replies with ok so the client knows the event was recorded. The root route (/) renders a list of the most recent events so you can see data instantly in the browser. Keeping everything in one file makes it easy to extend later.

index.js

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const PORT = process.env.PORT || 3000;

// In‑memory store for events (reset on restart)
const events = [];

app.use(bodyParser.json());

// POST /track – receive an event
app.post('/track', (req, res) => {
  const event = req.body;
  if (!event || !event.type) {
    return res.status(400).send('missing type');
  }
  events.unshift(event); // newest first
  // Keep only the last 20 events to stay lightweight
  if (events.length > 20) events.pop();
  res.send('ok');
});

// GET / – simple UI showing recent events
app.get('/', (req, res) => {
  const listItems = events.map(e => `<li>${e.type} – ${new Date(e.timestamp || Date.now()).toLocaleTimeString()}</li>`).join('');
  const html = `
    <!DOCTYPE html>
    <html lang="en">
    <head><meta charset="UTF-8"><title>Notch Metrics</title></head>
    <body>
      <h1>Recent Events</h1>
      <ul>${listItems}</ul>
    </body>
    </html>`;
  res.send(html);
});

app.listen(PORT, () => console.log(`🚀 http://localhost:${PORT}`));
4

Phase 4: Run locally

Now that the server code exists, we use nodemon to watch the file and restart automatically whenever you edit it. This speeds up development because you don’t have to manually stop and start the process after each change. The command below installs nodemon locally (if you haven’t already) and launches the app in watch mode.

Running the server will print a friendly URL to the console; open that URL in a browser to see the UI.

npm install nodemon --save-dev
npx nodemon index.js
5

Phase 5: Verify and extend

With the server running, send a sample event using curl to make sure the /track endpoint works. After the request, refresh the root page – you should see the new event appear in the list. From here you can add more event types, enrich the UI with a tiny chart library like Chart.js, or persist events to a file or database for longer‑term storage.

curl -X POST http://localhost:3000/track \
  -H "Content-Type: application/json" \
  -d '{"type":"page_view","timestamp":'$(date +%s%3N)'}'

Verifying the Stack

CheckCommand / Why it matters
Server starts without errorsRun npx nodemon index.js and see the console log 🚀 http://localhost:3000
POST request returns okExecute the curl command above; response should be exactly ok
UI shows the recorded eventOpen a browser to http://localhost:3000 and see a list item with type page_view

What’s next?

You now have a functional Notch‑Metrics core. Experiment by adding a tiny chart (e.g., Chart.js) or persisting user IDs. When you’re ready, wrap the UI in an Electron menubar or macOS SwiftUI menu bar to get a true “notch” experience.