← Projects

VetControlLog

Controlled substance log for U.S. veterinary clinics, built solo as a pnpm monorepo with a Next.js frontend, a NestJS and Prisma API, and an append-only ledger.

Company
Personal project
Role
Founder and Developer
Stack
Next.js React TypeScript Tailwind CSS NestJS Prisma PostgreSQL Cloudflare R2 Railway
Highlights
  • Built the product end to end, a Next.js 16 and React 19 web app plus a NestJS 10 and Prisma API, in a pnpm monorepo with a shared TypeScript package for the ledger math.
  • Designed the log as append-only. A database trigger rejects any update or delete on ledger and audit rows, and mistakes become linked correction entries.
  • Stored every volume as integer microliters and kept the ledger math in one tested shared package, so balances never depend on floating point.
  • Built PIN-signed entries, witnessed waste signed by a second member, and a read-only inspector role, with PDF and CSV exports.
VetControlLog landing page, controlled substance log for veterinary clinics

What it does

VetControlLog is a record-keeping tool for the controlled drugs a veterinary clinic keeps in its safe, Schedule II to V. Staff log each draw on a tablet and sign it with a PIN. Every container keeps a running balance. At a count, the app shows whether a gap looks like hub loss, a vial that held more than its label, or something to look into. It is a record-keeping tool, not legal advice, and it does not promise compliance.

It is live at vetcontrollog.com with a 14-day trial.

How it is built

  • Web: Next.js 16 (App Router), React 19, TypeScript and Tailwind CSS, with Radix-based UI components. Dose logging is built for a shared clinic tablet.
  • API: NestJS 10 with Prisma and PostgreSQL. Every query is scoped to the clinic of the signed-in member, and the clinic is never taken from the request body.
  • Shared package: the ledger math (balance after an entry, reconciliation, dose checks, loss deadlines, witness rules) lives in one TypeScript package with tests, used by both the web app and the API.
  • Infrastructure: Railway for the two services, Cloudflare R2 for files, Resend for email and Polar for billing.

Decisions I would call out

An append-only ledger. Ledger and audit tables are protected by a PostgreSQL trigger that raises an error on any update or delete. A correction is a new entry that points at the original one and carries a reason. Each entry is written in a single transaction that locks the container row, computes the next sequence number and balance, and chains a hash to the previous entry. A balance below zero is rejected, never clamped.

Integer microliters. Volumes are stored and summed as whole microliters and converted to milliliters only for display, which avoids float rounding in a log people need to trust.

Records are never deleted. When a trial or subscription ends, a clinic becomes read-only. Viewing and exporting still work, and new entries are blocked.

People and signing. Each entry records who signed it with a PIN. Waste needs a witness who is a different member. Wrong PIN attempts lock the account for a few minutes, and an inspector role gives read-only access.

Around the product

Four free tools (a hub loss calculator, a mg to mL calculator, a Form 106 deadline calculator and an inspection checklist), printable log and biennial inventory templates, and 21 articles on controlled substance record keeping, all in the same Next.js app.

Interested in working together?

Open for senior frontend roles, contracting, and engineering consulting.