EmployerPre-launch2024–present

Heartbeat Pharmacy Platform

Lead Developer

A full e-commerce platform for a Greek pharmacy, owned outright (no per-transaction SaaS bill), with AI shrinking SKU enrichment from days to hours.

Results

products in the first full sync
10,417
between stock updates
1 min
catalogue tools an AI assistant can call directly
69
Heartbeat Pharmacy Platform

Stack

  • Medusa 2.14
  • Next.js 16
  • PostgreSQL
  • MCP server
  • Anthropic SDK
  • Browser automation
  • Windows Service

The situation

Heartbeat is a Greek pharmacy with a physical shop, where stock already lived in the till system. They wanted their own online store, with no per-order commission going to somebody else’s platform, and without hiring a second person just to type product descriptions. They brought me on as the only developer on the project. Instead of one large program I delivered separate systems that work together, so any one of them can stop or be updated without taking the rest down.

A store they own

The code, the database and the server belong to the pharmacy. No platform takes a cut of each order, and no third party’s pricing change can raise the cost of running the shop overnight.

The Heartbeat Pharmacy homepage: a top bar with the pharmacy phone number and free-shipping terms, a wide search field, a row of category links, and a seasonal banner reading Ενέργεια για κάθε μέρα
The storefront: search, categories and the delivery terms all above the fold

A product page has to answer the questions a pharmacist gets asked at the counter. Price with VAT shown plainly, whether it is in stock today, how soon it arrives, what the shipping will cost to your address.

A product page for Lamberts Natural Pea Protein: gallery thumbnails on the left, the large product image in the middle, and on the right the price of 33,87 euro with VAT noted, a buy button, the stock line and a shipping calculator
How it works
  • Store: a Next.js 16 storefront on a Medusa 2 backend, with PostgreSQL, Redis and Meilisearch.
  • Search: Meilisearch through InstantSearch, results arriving as the customer types.
  • Checkout and mail: a bank gateway integration, Turnstile on the forms, and order email through Resend with React Email templates.
  • Monitoring: PostHog for usage and Sentry for errors only, with personal data scrubbed before any report leaves the server.

One person, thousands of product codes

Nineteen top-level categories, each with its own subcategories, is a lot of shelf to keep tidy for a pharmacy that also has a counter to run.

The categories page: cards for Makeup, Perfumes, Mother and Baby, Vitamins and Supplements, Slimming and Sport, and Pharmacy, each listing its subcategories as pills with a plus-ten-more link

The catalogue has its own system, separate from the store. One person picks products, asks for a draft description, checks the text and the images, and only then does the product reach the storefront.

The point was never to have the machine write instead of a person, but to stop the person starting from a blank page on every single code.

Pixel art illustration of a product management screen: filters on the left, a product list with tick boxes in the middle and an edit form on the right
The catalogue: filters, bulk selection and editing on one screen
How it works
  • Catalogue backend: Express 5 with TypeORM over two separate PostgreSQL databases, one for data and one for logs only, so audit-record noise never weighs on the catalogue.
  • Enrichment: the Anthropic SDK and Google GenAI for drafts, Puppeteer 25 with in-house evasions for catalogues that expose no API, BullMQ queues on Redis for bulk jobs, and S3 for images.
  • Admin: React 19 with Vite and TanStack Router, Query and Table, a Tiptap text editor and Recharts dashboards.
  • Checks: the integration tests bring up a real PostgreSQL in Docker with Testcontainers on every run, no mocks.

The shelf and the screen agree

Stock lives in the pharmacy’s own system, not on the site. A service reads it and sends it to the store every minute, so nothing gets sold that is not on the shelf.

Pixel art illustration of the data flow: shelves of records on the left, a server with a Medusa head in the middle and a shop window on the right, with arrows pointing rightwards
From the pharmacy's own database to the server, and from there to the storefront
How it works
  • First sync: a full one, all 10,417 products; after that only what changed since last time travels.
  • Resilience: retries with growing backoff, a lock so two syncs never run at once, a circuit breaker, a DRY_RUN mode for testing without writes, and logs that rotate daily.
  • Packing: a separate service asks every 30 seconds for new courier vouchers and prints them on the packing bench’s thermal printer; the picking list carries the customer’s name only, never the address.
  • Prices: all price arithmetic lives in one dependency-free package pinned to a version and shared by the backend and the admin, so there are never two versions of the same calculation.
  • Kleidarakos B2B & Papanikolaou Doors: another large commerce build, but B2B instead of B2C
  • pooq.gr: WooCommerce commerce engineering, extending a store instead of replacing it
Get in touch

Display preferences

Text

Text size
Line spacing
Letter spacing

Color

Contrast

Motion and images

Reading

Reading aid
No changes