Bartosz Bartosik

Curriculum Vitae

SME IT Support Expert

Databases / Monitoring / IAM / SQL

Docker / Kubernetes / Python / AI / ML

Project Management

ITIL / IT Service Management

Knowledge Management

Compliance & Risk Management

Blog Post

MokreNoski: pet health diary which your vet can actually read

2026-09-09 Projects
MokreNoski: pet health diary which your vet can actually read

Live: mokrenoski.app

MokreNoski is my own product, not project for a client. It is health diary for pets (cats, dogs and other species) with a report for the veterinarian. The idea came from watching how people take care of chronically sick animals. Notes in the phone, paper on the fridge, photos of lab results lost somewhere in the gallery, and apps which support only cats. Then the vet asks “since when he drinks more water?” and the owner scrolls and guesses.

I decided to build one place for timeline, medications and the vet report. I built it on my own backend, so I control the data and every part of the system, which I understand from top to bottom.

Stack

  • FastAPI (Python 3.12), SQLAlchemy 2, Alembic
  • PostgreSQL
  • Redis + Celery for background jobs (PDF, emails, notifications)
  • React 18 + Vite + TypeScript + Tailwind (owner panel and separate admin panel)
  • React Native / Expo (mobile app)
  • ReportLab for PDF generation
  • S3-compatible object storage
  • Docker Compose, GitLab CI/CD

1. Double dose of medicine is blocked by the database, not by memory

Before: two people in one house. One gives the pill in the morning, the second one does not know and gives it again. Reminder apps only remind you. They have no idea who already gave the dose.

Now: every scheduled dose is a separate slot, and the database has a unique constraint on medication + slot. When second person tries to mark the same dose, they see it is already given.

Why better: the check lives in PostgreSQL, not in the app. So it works even when two phones click in the same second. For a cat on insulin this is not a “nice to have”.

2. Vet report which you can share and also take back

Before: screenshots, or a PDF sent by email which lives forever in somebody’s inbox.

Now: one click generates a PDF (for example last 90 days) in a background worker: symptoms, weight, medications, chronic disease values, visits. The owner can also share it as a link. The link has a limit of views and there is a button to revoke it any time.

Why better: the vet gets a clean, ordered document instead of “I think it was few days ago”. The owner still controls the medical data of their pet after sharing.

3. Chronic diseases have their own modules

Before: cat with kidney disease means creatinine, SDMA, phosphorus, fluids under the skin, urine. All of it in Excel, or nowhere.

Now: dedicated logs for CKD and diabetes (urine checks, blood results, insulin doses, water intake, weight) and lab panels as a separate type of entry. When the vet writes a recommendation after a visit and there is no active treatment plan for it, the app flags it. So nothing from the visit gets forgotten.

Why better: values are structured, so they can go on charts and into the report. With free text this is not possible.

4. More than one species, more than one person

Before: popular apps in Poland were cat-only. “Sharing” a pet meant sharing your password.

Now: species live in a registry in the database, each with its own breeds. Polish catalogs (vaccines, diseases, toxic substances, procedures) are versioned seeds in the repository. There are 9 catalogs at the moment. Family members join a household with their own accounts. A pet-sitter gets a separate invite for the time of holiday.

Why better: everybody has their own login and role. The history shows who wrote which entry.

5. Offline on the phone without duplicated entries

Before: the vet clinic is in a basement, there is no signal, so the entry is written “later”, which means never.

Now: the mobile app keeps operations locally and sends them when the connection comes back. On the server every client operation has a unique ID per user. If the phone sends the same thing two times, the backend recognizes it and does not create a duplicate.

Why better: the sync is safe to retry. Bad connection does not mean doubled or lost data.

6. QR on the collar, with privacy settings

Before: a tag with phone number, or nothing.

Now: each pet can have a QR code for the collar which opens a public “lost pet” page. The owner decides what is shown there, for example whether to show medical alerts (important when the dog needs medicine) and part of the chip number. The public token can be rotated, so an old printed tag stops working.

Why better: the finder sees what helps, and nothing more.

7. Database changes and deploy are code, not manual steps

Before: in typical small project, deploy is “log in, git pull, restart and hope”.

Now: the schema is described by 90 Alembic migrations, all reviewed in git. The pipeline has 7 stages: security scan (secrets, Python code, npm dependencies), lint, tests, build of Docker images, verify, push and automatic deploy. The verify stage starts the full stack and runs all migrations on a fresh database before anything goes to production.

Why better: a broken migration or a leaked secret stops the pipeline, not production.

The ending

The best proof of point 7 came by itself. One day the pipeline went red, but not because of my code. The official MinIO image simply disappeared from Docker Hub. All migrations passed, and the job stopped when it tried to pull the storage container. Fix was one commit: a community fork, pinned to a specific version, in all compose files. Production never saw the problem, because the pipeline caught it first.

👉 Check it live: mokrenoski.app


Tech stack in details

Frontend. The owner panel and the admin panel are two separate React 18 apps built with Vite and TypeScript, styled with Tailwind. Admin runs in its own Docker container, so it is not part of the public bundle at all. Data fetching and caching goes through TanStack Query, and translations through i18next. A script in the repository checks that the admin translation files are complete, so a missing key is visible before the release. The mobile app is React Native with Expo. It shares the same REST API with the web, and it has local storage of operations for offline work. Charts and the PDF report are built from the same structured data, so what you see in the app is what the vet sees on paper.

Backend and database. The API is FastAPI on Python 3.12 with SQLAlchemy 2 models and PostgreSQL. Content types are not generic “posts” but real domain tables: check-ins, symptoms, medications with dose slots, lab panels, vet visits with recommendations, treatment plans, households and share links. Important rules live as database constraints, not only in the code. One example is the unique dose per slot, another is the unique client operation for sync. Catalogs like vaccines, diseases and toxic substances come from versioned JSON seeds. Heavy work (PDF generation in ReportLab, emails, notifications) goes to Celery workers with Redis as broker. Files are kept in S3-compatible object storage. Payments go through Stripe and RevenueCat, with signature-verified webhooks and an idempotency log, so the same event is never processed two times.

Infrastructure. Everything runs in Docker Compose, with separate compose files for development, registry images and production, kept in sync on purpose. The GitLab pipeline goes scan → lint → test → build → verify → push → deploy. The scan stage looks for secrets in git and for known problems in Python code and npm dependencies. Ruff and the frontend linters run in lint, and pytest in test. In verify, the full stack starts on fresh containers and all Alembic migrations run from zero, which catches problems like two migration heads before production. After the images are pushed, deploy is automatic by webhook. Python dependencies are locked with uv, so every build installs the same versions.


Tags: