PetsFirst sells products through Shopify, and for retail that’s the right tool. The healthcare side of the brand didn’t fit. Booking a checkup, tracking vaccinations, scheduling surgery: these are services with state, calendars, and records, and a storefront has no honest way to model them. That’s how PetsFirst Health ended up a custom build on Next.js, Node.js and Express, and MongoDB, with a Flutter app alongside.
Appointment logic can be bolted onto a store with apps and workarounds, and I considered it, since the team already knew the platform. What stopped me is that every workaround encodes commerce assumptions into a domain that isn’t commerce. An appointment isn’t a product with a variant, and treating it as one means fighting the platform on every feature that follows. Once a domain diverges that far from what the platform models, the custom build is the cheaper path, and it doesn’t take long for that to show.
The decision that shaped the architecture most: the Next.js web platform and the Flutter app consume the same Node.js and Express API. No mobile-only endpoints, no logic drifting into either client. The pressure to special-case the mobile app is constant, because a slightly different endpoint would always be quicker today. Holding the line means a feature is built once and appears on both clients, and the API stays something one person can reason about.
The data is document-shaped. A pet has records, appointments, and history that nest naturally, and the three service lines each grew fields of their own as the product developed. Documents absorbed that without a migration forcing the pace every time a service changed shape.
The platform runs on AWS with CI/CD from GitHub. Serving a mobile app raises the bar on API stability: the web front end can be redeployed in minutes, but a breaking change against the app stays broken until users update. That constraint does more for API discipline than any style guide.