Intro
Respecting user privacy has always been important. And with GDPR, CCPR and other laws it became mandatory for all of us. While privacy is important it’s also a vital interest for online businesses to know how users are using their apps and websites. This allows businesses find flaws in usability and optimize the website to provide the best experience for their clients.
But how can you track users without violating their privacy?
With Privatracker we are providing a solution. Privatracker is a SaaS tool designed to serve as a privacy-respecting alternative to traditional web analytics platforms like Google Analytics. It’s a tool that tracks users anonymously. It’s not possible to track individuals. But it is possible to see what is happening on the site. Which pages are popular. Where users are coming from (country etc), and who is sending the users to your page (Google, Linkedin etc).
Unlike Google Analytics, Privatracker allows businesses to track web activity without requiring intrusive cookie banners(!), offering a seamless user experience while maintaining compliance with privacy regulations.
But this post is not about privacy. This post is about how to architect such a service. Saving some clicks and aggregating data should not be too hard. And that is right. But with the wrong tools it will go wrong - and a traditional storage layer like PostgreSQL will NOT work.
Let’s check out the stack we’ve used and let’s discuss the technologies.
The Challenge
Our goal was to develop a tool that was both simple and scalable, accommodating everything from small startups to large enterprise clients. Another requirements was to enable enterprise clients to log in securely, which added another layer of complexity to our project. We needed a robust tech stack that could handle these demands while remaining lightweight and efficient.
The Tech Stack
To meet these challenges, we are currently using the following tech stack.
Storage layer for TimeSeries Data
PostgreSQL provides the reliability and performance needed for managing complex data structures. It’s a proven solution with strong community support and extensive features that cater to a wide range of database needs.
While PostgreSQL works fine for traditional data like users and groups, it does NOT work well when you have to aggregate vast amounts of data for periods of time.
That’s why are using TimescaleDB for handling time-series data. Because TimescaleDB runs as an extension on top of PostgreSQL we don’t have to learn a new query language or any other tools to backup the data or run our servers. TimescaleDB allows us to efficiently store and query large volumes of time-series data, making it ideal for our analytics needs.
Authentication
For federated login, we utilize Keycloak. It simplifies authentication and authorization for users, providing features like single sign-on and identity brokering. However, it’s another system to maintain, and we are considering whether it’s essential for our needs.
Backend
Our backend is powered by a simple Node.js Express server. We are using Javascript and mostly TypeScript. Javascript (and Typescript) are amazing, because they allow you to use the same tools on the frontend and backend. Great for knowledge sharing between teams. Great for code sharing.
Frontend
The frontend is developed using React. Works like a charm. We are just using some simple css to style the components. Tests are done using jest.
Deployment
We deploy Privatracker as a single unit using Dokku, a minimalist PaaS that allows us to use a “push-to-deploy” strategy. This simplifies our deployment process and ensures a consistent environment across all instances of our application. The whole application is written as 12 Factor app. This means we could move off of Dokku easily and use a hyperscaler like AWS / GCP or even a big fat Hetzner box with ease.
Discussion
TypeScript/JavaScript
While they are the de facto languages for web development, we found some drawbacks. Typescript is ok, but it’s “just” a decoration on top of Javascript. It’s too easy to mess up your code because the type-checker of Typescript simply does not understand everything. Unexpected values can slip through too easily in our opinion.
The Javascript ecosystem is very quickly evolving - which is amazing. But this also means that libraries often have breaking changes and you have to be very alert to updating and potentially rewriting your codebase (This is also true for the frontend). We are discussing a rewrite to Go right now because we could rewrite the whole application with zero dependencies in Go. Potentially even simpler and faster.
React’s Complexity
React is undeniably powerful, but its complexity can be a burden. The feeling is that React was nice some time ago, but with the push to move away from class components to “pure” functions it does not feel lightweight any more. Surpsisingly - pure functions also need a state (!) and there are now so many workarounds aka hooks (useEffect, useState etc) that the code became harder to understand and test. useEffect can be error prone at times (why was my component rendered twice?).
We are considering moving to lit (wrapper around web components) or even pure web components and no build js.
Conclusion
Building Privatracker was a nice journey of balancing simplicity with functionality. Our commitment to privacy and user experience drives us to continuously refine our tech stack and explore new solutions. We believe that respecting user privacy should be effortless and are dedicated to providing a tool that makes privacy-respecting web analytics accessible to all businesses.
For more information about Privatracker and to see how it can help your business respect user privacy, visit privatracker.com.