Vital
- NestJS
- PostgreSQL
- Prisma
- React
- MUI
- Redux Toolkit
- Arduino
- Capacitor
- Mercado Pago
- AFIP
Family gym · Production system · Argentina
My family's gym running on a single system: members walk in by swiping their credential at the turnstile, pay and get invoiced without friction, and train with their routine on their phone. Three applications, one source of truth.
What problem does Vital solve?
Vital is my family’s gym. Like any neighborhood gym that grows, for years it ran on a mix of a spreadsheet, a notebook at the front desk and a few WhatsApp groups. With several hundred active members, that stopped working: nobody knew for sure who was paid up, the door was opened by someone eyeballing it, invoicing was a manual month-end chore, and members had no tool of their own beyond texting their coach.
I built it for our real operation — one system covering the three faces of the business at once: front-desk operations (who’s in, who owes, who’s booked), tax compliance (real payments and invoices, wired to AFIP and Mercado Pago) and the member experience (their routine, their progress, their access). The challenge wasn’t any of those pieces on its own — it was making them live on top of a single data model without duplicating the truth.
How is Vital built?
I built it as a monorepo with three independent applications sharing one API. At the center, a NestJS backend on PostgreSQL and Prisma, designed event-driven: modules don’t call each other by hand, they emit events and others react. When the turnstile reads a credential, it kicks off a chain — verify access → record attendance → notify — without coupling the hardware to business logic.
Each surface has a distinct user and purpose, but they all talk to the same API. That was the structural decision the rest rests on: a single source of truth for memberships, payments, attendance and routines.
What technical decisions underpin Vital?
The parts of Vital I care most about aren’t the screens — they’re the systems that connect those screens to the physical world and to the tax authority.
A turnstile that knows who you are
Access isn't a button — it's real hardware. A lightweight headless service talks over the serial port (serialport) to an Arduino that controls the turnstile. When someone swipes their credential, the system checks the API for an active membership and only then opens the gate — with no desktop app in the middle.
- Credential read via Arduino → NestJS event → access verification decoupled from the hardware
- Alternative QR check-in: single-use tokens that expire after 5 minutes, generated from the member app
- Every entry is recorded as attendance and fires notifications without blocking the gate
Charging and invoicing for real, in Argentina
A gym that invoices seriously has to talk to AFIP (the tax authority) and to a payment provider. Vital integrates AFIP electronic invoicing over SOAP (with certificate handling) and Mercado Pago payments, including idempotent webhooks so the same payment is never processed twice.
- AFIP electronic invoicing with certificate validation and its own diagnostics
- Mercado Pago payments plus a processed-webhooks table to guarantee idempotency
- A WhatsApp bot (via Twilio) that lets staff issue invoices by chatting from their phone, driven by a per-session state machine
Debt that chases itself
The money a gym loses is almost always money nobody went to collect. So dunning is its own subsystem that runs on cron and escalates by itself: it retries first, then reminds, then grants grace, and finally suspends — with no one having to remember.
- A scheduler with three stages: process retries → send reminders (email/SMS) → suspend on grace-period expiry
- Membership traffic-light (active / expiring / expired) as the central state across the app
- Multichannel notifications: email (SMTP), SMS (Twilio) and push (Firebase) depending on the event
Train, not just pay
The members' PWA — packaged with Capacitor for iOS and Android — is what keeps the system from being purely administrative. Members see their assigned routine, run it block by block and log every set; the system keeps their history and detects personal records.
- Public routine catalog + per-member or per-group assignments, with days, blocks and exercises
- Workout session logging: sets, loads and automatic personal records
- Class booking, health metrics, QR check-in and push notifications
The three surfaces
React 19 + MUI for the front desk. Designed as a task inbox, not a dashboard: ⌘K check-in, payments and membership status with direct actions on every row.
Capacitor PWA: routines, progress, classes and QR access, publishable to the stores as a native app.
Event-driven NestJS on PostgreSQL/Prisma. One source of truth for the surfaces, the external integrations and the turnstile over serial — with no extra layers.
Closing
Vital is the project where end to end really shows what it means: it doesn’t stop at a nice screen, it ends at a turnstile that opens, an invoice that’s valid before the tax authority, and a member logging a set from their phone. Three applications, one data model, and a pile of decisions — event-driven design, payment idempotency, automated collections, hardware over serial — that exist because the system runs in my family’s gym, every single day. Even access control ended up minimal: the first version was a desktop app that turned out to be overkill, replaced by something far smaller that does exactly what’s needed.