A minimal Node.js CLI that logs and queries events locally
Welcome to another official UVF IT guide! In this quick build you’ll create a tiny terminal‑based analytics tracker inspired by Databox CLI, using only Node.js and a local SQLite database—no external services required.
We’ll scaffold a tiny project, add a simple data model, expose a couple of commands, run it locally, and verify that you can record and retrieve events in under 45 minutes.
Initialize an npm project and install the only dependencies you need: sqlite3 for DB access and commander for CLI parsing.
mkdir terminal-analytics && cd terminal-analytics
npm init -y
npm install sqlite3 commanderCreate a helper that opens (or creates) a SQLite file and ensures an events table exists.
src/db.js
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';
export async function getDb() {
const db = await open({
filename: './analytics.db',
driver: sqlite3.Database
});
await db.exec(`CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
payload TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`);
return db;
}In this step we expose two useful commands for our analytics clone. The track <name> command records a new event in the SQLite database, while list fetches the most recent entries so you can see what has been logged. We’ll use the popular commander library to parse arguments and keep the code tidy, and better-sqlite3 for a simple synchronous DB interface.
Both commands are defined in the same entry point file, src/index.js. By keeping the logic here we avoid extra files for this minimal clone, making it easy to read and modify as you grow the tool.
src/index.js
#!/usr/bin/env node
const { Command } = require('commander')
const Database = require('better-sqlite3')
const path = require('path')
// Initialise SQLite DB in the project root
const dbPath = path.resolve(__dirname, '..', 'analytics.db')
const db = new Database(dbPath)
// Ensure the events table exists
db.prepare(`CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
timestamp TEXT NOT NULL
)`).run()
const program = new Command()
program
.name('track')
.description('Minimal terminal analytics tracker')
.version('0.1.0')
program
.command('track <name>')
.description('Log an event with the given name')
.action(name => {
const stmt = db.prepare('INSERT INTO events (name, timestamp) VALUES (?, datetime("now"))')
stmt.run(name)
console.log(`✅ Tracked event: ${name}`)
})
program
.command('list')
.description('Show the most recent 10 events')
.action(() => {
const rows = db.prepare('SELECT id, name, timestamp FROM events ORDER BY id DESC LIMIT 10').all()
if (rows.length === 0) return console.log('No events recorded yet.')
rows.forEach(r => console.log(`${r.id}: ${r.name} @ ${r.timestamp}`))
})
program.parse(process.argv)Now that the CLI code is ready we hook it into npm so you can call it from anywhere on your machine. Adding a bin entry and a simple script lets you test the commands without publishing the package first.
After linking globally, try track hello and then list to see the output. This confirms the whole pipeline – command parsing, DB write, and read – works as expected.
package.json
{
"name": "terminal-analytics-clone",
"version": "0.1.0",
"main": "src/index.js",
"bin": { "track": "src/index.js" },
"scripts": { "test": "node src/index.js list" },
"dependencies": { "commander": "^11.0.0", "better-sqlite3": "^9.0.0" }
}npm install
npm link # makes `track` available globally
track hello # should print a ✅ message
track world # log another event
track list # typo intentional – shows help
track list # correct usage: `track list` shows recent eventsWith the CLI wired up, let’s double‑check that the SQLite file actually contains our data. The sqlite3 CLI is perfect for a quick inspection and gives you confidence the persistence layer works.
From here you can start thinking about extensions: adding optional tags, storing JSON payloads, or exporting the log to CSV for external analysis. Those ideas are left as exercises for the next iteration.
# Show the DB file location
ls -l analytics.db
# Query the row count directly
sqlite3 analytics.db "SELECT COUNT(*) FROM events;"
# Peek at the first few rows
sqlite3 analytics.db "SELECT id, name, timestamp FROM events ORDER BY id LIMIT 5;"| Check | Command / Why it matters |
|---|---|
Global command track runs without error | track hello prints a ✅ message |
list shows the recorded event | Console output includes a line like 1: hello @ … |
| SQLite file exists and holds rows | sqlite3 analytics.db "SELECT COUNT(*) FROM events;" returns a number >0 |
Your minimal terminal analytics tracker is now functional. Feel free to experiment – add JSON payloads, integrate a remote webhook, or package it for distribution with npm publish. Happy hacking!