Skip to content
Work
HIGHBackend / NestJS API platform01/2025–present

Home API

NestJSTypeScriptNode.js 22PrismaPostgreSQLpgvectorXenova/all-MiniLM-L6-v2fast-fuzzyFirefly III APIJWTSwaggerLaTeXAltaCVTeX LiveDocker
github.com/itd24/home-api

Overview

This is the backend of my home server. The dashboard, the automation workflows and the document scanner all talk to it. It is a NestJS API written in TypeScript, with PostgreSQL behind it.

The most interesting part is the matcher. When a bank transaction comes in, the API turns its description into a vector with a small model that runs inside the API itself. It compares that vector with the ones it has seen before and mixes in a fuzzy text check. If the best match is good enough, the job is done and nothing is paid for.

If the match is weak, the transaction goes to an AI classifier in n8n. The answer comes back and is saved as a new example. So the next time a similar transaction shows up, the API already knows what to do.

The API also starts the scanner, keeps track of scanned documents, stores the car’s parking spot and renders LaTeX documents to PDF.

Problem

My home server needed one backend for the dashboard, the automation workflows and the document scanner. It also had to label bank transactions automatically. I did not want to pay for an AI call on every transaction, and I wanted the labelling to get better the more I used it.

Approach

  • I wrote one NestJS API in TypeScript on Node 22, with PostgreSQL behind it through Prisma.
  • I split it into seven modules: auth, users, documents, cars, banking, matching and latex.
  • I made the matching run inside the API with a small local model, so the first step costs nothing.
  • I sent only the hard cases to an AI classifier, and I wrote its answers back as training data.
  • I gave humans and automation different ways to log in.
  • I wrote the matching rules and thresholds in their own README next to the code.

Architecture and design decisions

  • The dashboard logs in with a JWT. Automation jobs use static API keys. Both live in the same auth module.
  • The documents module triggers the physical scanner and keeps track of every scanned document. The scanner service runs separately on port 4356.
  • The banking module talks to Firefly III and stores extra information about each transaction.
  • The cars module keeps the parking location of the car.
  • The matching module turns a transaction description into a vector with 384 dimensions. It uses the local model Xenova/all-MiniLM-L6-v2 inside the API process. The vector is stored with pgvector and compared by cosine distance.
  • A fuzzy string comparison is mixed into the score to reorder the closest candidates.
  • When the best score stays below 0.75, the transaction goes to an AI classifier. That classifier runs outside this repository, in n8n. Its answer is posted back to the examples endpoint and becomes training data for the next import.
  • The latex module renders AltaCV documents, such as a CV, to PDF. The Docker image bundles Node 22 with TeX Live and AltaCV, so rendering runs in the same image as the API.
  • Swagger is served under /api.

Reliability and performance

  • Matching runs in the API process with a local model, so it works without any outside service and costs nothing.
  • Only low confidence cases go to the AI classifier, which keeps the number of paid calls small.
  • Every classifier answer is stored, so the same merchant needs no second guess.
  • The thresholds, the scoring and the decision table are documented in one place.
  • Separate login methods keep the automation keys apart from the user login.

Outcomes

  • One API sits behind the dashboard, the automation workflows and the scanner.
  • Bank transactions are matched with vector similarity and no per call cost.
  • The matcher improves with use, because its own corrections become examples.
  • A CV can be rendered to PDF by the same service.
  • The public repository has an interactive API description through Swagger.

Known gaps

  • I have not measured how often the matcher is right, or how many transactions go to the AI classifier.
  • I do not publish speed or memory figures for the local model inside the API.
  • The 0.75 threshold is a value I chose. I have not shown that it is the best one.
  • The AI classifier is not in this repository. It lives in my n8n workflows.
  • The API is built for one household. I have not tested it with many users or heavy traffic.
  • The page does not say which licence the repository uses.

Get in touch

Hiring for architecture, technical leadership or applied AI engineering?

I answer questions about any of the case studies in detail, including the parts the source material does not cover.