Published: June 22, 2024
How I built a simple analytics system in less than 20 lines of JavaScript with Supabase
I wanted to see if I could build a basic, non-invasive analytics system with Supabase. Answer, I could.
I don't have Google Analytics or anything like that on my personal websites. I have no idea how many people read this blog, for example. However, I am in the process of working on some small products that I want to launch over the coming weeks and months and thought it would be good to have some basic analytics to see how people got to my product pages.
Things I wanted to track include
- Which website (I'll have a domain/sub-domain per product)
- Which page on the website
- The time the visit happened
- Where the visitor came from (Google, LinkedIn, etc)
Things I did not want to track include
- Bounce rate
- Where the visitor went next
- How long they stayed on the site
- Anything identifying (such as an IP address, country/region, etc)
Creating an analytics system in 20 lines of JavaScript
I could achieve this in about 20 lines of JavaScript using Node.js and Supabase. Here's how:
I'm using app.js
as my entry file in a simple node + express app to build my product website. It has 2 pages - a home page (to sell the product) and a 'success' page that you get redirected to after you have bought the product.
The code
In my app.js file, I have the following code for supabase:
// app.js
require('dotenv').config();
// Supabase
const { createClient } = require('@supabase/supabase-js');
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);
async function trackPageVisit(req, res, next) {
const acceptHeader = req.get('Accept');
if (acceptHeader && acceptHeader.includes('text/html')) {
const time_arrived = new Date().toISOString();
const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
const website = new URL(fullUrl).hostname;
const page_visited = new URL(fullUrl).pathname;
const came_from = req.get('Referer');
const { data, error } = await supabase
.from('stats')
.insert([
{ time_arrived: time_arrived, website: website, page_visited: page_visited, came_from: came_from }
]);
if (error) {
console.error('Error inserting data into Supabase:', error.message);
}
}
next();
}
app.use(trackPageVisit);
Explaining the code
Let's break it down.
We are placing all this code in app.js so that it runs on every page of the website.
The first few lines are simply declaring the Supabase variables to create the Supabase client. After that we have an async
function that will get the data we want and pass it to Supabase.
We create a variable for acceptHeader
and then check that the headers that get sent include the string text/html
. This ensures we are not tracking files we don't want to track, such as CSS, JS, images, and things like that. We only care about HTML pages.
Next we create variables for things like what time the visit happened, what website was visited (this is so that I can have the stats for all my product sites in the same database table and can query them together easily), where they came from ('Referer'), and what page was visited.
Once we have those variables, we then call Supabase and in the stats
table of the database that is defined by process.env.SUPABASE_URL
, we write
using key: value
pairs for the above variables. If there's an error we log this to the console.
Then we call the next()
function to tell this function to go to the next middleware or route handler.
That's the function completed. We then call it via app.use()
so it runs on each page of the site.
When this runs, it will put info into the database like this:
- website: example.mark.ie (the name of the site visited)
- page_visited: /success (the page on the site that was visited)
- came_from: https://www.linkedin.com (I only track the site it came from, not the specific URL/post/tweet)
- time_arrived: 2024-06-24 09:45:03.23+00 (the time the visit took place)
Viewing the stats
Now, when I login to Supabase, I can go to my statistics database, then click on the table editor and view my stats from within the stats table.
It's a very simple stats system, but gives me the information I need without being invasive to any visitors and sends absolutely no data to any third party processors such as Google.
If it takes my fancy, I might write a small dashboard app for it next so I can visualise the data to make it a little easier for me to process. I imagine that could be an interesting project for next weekend, using Next.js, Supabase, and Chart.jsĀ