Dartobert

A darts scoring app I built because existing apps kept throwing away the data I actually wanted. Every throw is stored. Every stat is calculable.

Tech Stack

Angular 21NestJS 11TypeScriptPostgreSQL 16WebSocketsDockerKubernetesGitHub Actions

Angular because it's the framework I know best — and a mobile-first SPA with Angular Material felt right for a game tracking app. NestJS keeps TypeScript across the entire stack. PostgreSQL because I need a real SQL engine to run arbitrary stats queries over the full throw history.

Dartobert is a full-stack darts game tracking app I built from scratch — because every app I tried either made scoring awkward for groups, or quietly discarded the data I actually wanted to analyze.

Why I built it

Two things frustrated me with existing darts apps:

Shared scoring is a mess. Most apps are designed for one player, on one phone. If you’re playing with friends, everyone either needs their own device or you pass one phone around. That kills the flow of the game. Dartobert puts scoring on a single shared device — anyone can enter their throw, and each player still gets their own account and stats after the game.

Stats get thrown away. Almost every darts app aggregates data at recording time. They store your average, not your throws. Once the game is done, that’s it. I wanted the opposite: every single dart in the database, forever. That way I can calculate anything, retroactively, without having to decide upfront what metrics matter.

What it does

The core loop is simple: create a game, add players, enter throws. But there’s a lot built around that:

How it’s built

The backend is a NestJS 11 REST API with WebSocket support, using TypeORM and PostgreSQL 16. Feature modules handle auth, games, players, statistics, tournaments, leagues, notifications, and more. Migrations are required — no synchronize: true in production.

The frontend is an Angular 21 SPA using Angular Material with a custom dark theme, optimized for mobile (max-width 420px default). Newer components use Angular signals for state; RxJS stays for HTTP and WebSocket streams.

Everything runs in Docker containers on a Kubernetes cluster, with separate images for frontend and backend. GitHub Actions handles CI and builds.

Current state

Started in September 2024. Now at v0.24, actively maintained. The most recent version added per-player time statistics, public profile filter controls, and a significant backend performance overhaul (up to 10x faster statistics endpoints).

The app is running — I use it with friends regularly. Turns out we are better software developers than dart players.

Related posts

Back to Projects