PetService: Pet Care Platform on NestJS/React
PetService needed a platform built from a written spec up: client and pet records, and a role structure covering not just staff but an entire franchise network. We ran discovery with the client to turn their requirements into a technical spec, then built both the backend and frontend from that spec.
The Challenge
The brief started as a conversation, not a spec. We interviewed the client directly to define scope for both the frontend and the backend before writing any code, then translated that into a data model covering eight distinct roles, from superadmin down to individual franchise workers, each with its own permissions and views.
A franchise business isn’t a single admin panel with users under it. Franchisees run their own locations, franchise managers oversee several, vendors and workers sit outside that chain, and every one of those roles needed its own slice of the same data without stepping on another role’s records.
What We Built
Backend (NestJS)
- Eight-role structure: superadmin, admin, account manager, franchisee, franchise manager, vendor, worker, and customer, each scoped to its own permissions
- Animal records: model, migrations, and CRUD for pet profiles, with animal types, linked to a user’s profile
- Address module with per-profile address management, separate from the core user record
- Authentication with registration, login, session management, and password recovery, sessions listed and revocable per device
- File service for uploads and document storage
- Mailer service for transactional email
- Swagger-documented API with DTOs across endpoints
- Sequelize ORM on a Dockerized environment with ESLint and Prettier configured from day one
Frontend (React)
- Redux Toolkit and RTK Query for state and data fetching, with a request interceptor wired in from the start
- Reusable data table template: sortable columns, pagination, and per-row actions, first built out for the superadmin’s admin-management console
- Authentication pages: login and registration, both localized

- A superadmin console for managing the admin account list: create, filter, and remove admins directly against the live API

- Create and delete flows for admin accounts, each behind its own confirmation modal


- Inline, per-column filtering on the reusable table: pick a field, an operator, and a value, no separate search page

- A separate all-users view for the plain admin role, listing every account across the platform with its role assignments

Customer profile
The backend already had full data models for animal records and addresses, with no screens to manage them. We built the customer-facing side: a profile page where a customer edits their name, adds and edits pets with type, breed, age, size and coat length, and manages saved addresses with coordinates and a plus code.


Building against the address endpoints surfaced a real bug in the API: Address had no reverse Sequelize association back to User, so every address read after the first write threw SequelizeEagerLoadingError: User is not associated to Address!. Creating an address worked because that path used the association already declared on User; every other address endpoint didn’t have one to use. We added the missing @BelongsToMany on the Address model to fix it.
Delivery
Frontend and backend environments were set up and built in parallel by separate leads, syncing through direct client calls rather than a one-way spec handoff. Core entities landed first: the role structure, animals, addresses, and sessions. The reusable data table and the superadmin console came next, once the backend’s admin-management endpoints were stable enough to build a real screen against.