Andrew Forster
Product-minded software engineer
contact@andrewjf.com
contact@andrewjf.com

2026 All Rights Reserved
  1. Home
  2. Projects
  3. DinoQuote
DinoQuote project preview

DinoQuote

TypeScript
React
Vite
Tailwind CSS
TanStack Query
Node.js
Vercel
PostgreSQL
Prisma
Supabase
Stripe
<h2>From fast-moving prototype to production platform</h2><p>DinoQuote is a multi-tenant sales platform for HVAC contractors. It gives contractors an instant quoting tool, lead management, a filter storefront, maintenance plans, analytics, and integrations with the systems they already use.</p><p>I joined after the first version had been built quickly to prove the idea. It had real product breadth, but that speed left the team with a browser-heavy data model, inconsistent application boundaries, fragile authorization, duplicated frontend patterns, and limited visibility when something failed. My job was not simply to add more features. I took ownership of turning the application into software the team could safely operate, debug, and continue building.</p><h2>Rebuilding the application boundary</h2><p>The original frontend still read and wrote much of the product data directly through Supabase. That made authorization depend heavily on scattered client behavior and database policy, while business logic was split between the browser and dozens of edge functions. It was difficult to reason about where a request was validated, which tenant it belonged to, and what would happen when the product changed.</p><p>I moved product reads and writes behind server-owned Vercel APIs, organized the backend around services and repositories, and migrated application data access to Prisma. Supabase remained where it was useful for authentication, storage, and realtime behavior, but the browser no longer had direct access to application data. By the end of the production migration, the application inventory reported 271 Prisma-complete server files, no mixed data-access files, no remaining server product calls through the Supabase Data API, and no browser calls directly to the database or legacy edge functions.</p><p>This was an incremental replacement, not a clean-room rewrite. Product flows had to keep working while callers moved one area at a time. I added route and migration tests around tenant isolation, administration, leads, notifications, purchases, signup, and impersonation before retiring the superseded runtime paths.</p><h2>Making multi-tenant security explicit</h2><p>DinoQuote serves contractors, their teams, platform administrators, and customer-facing experiences from the same system. A simple admin flag was not enough for that model.</p><p>I replaced broad role checks with explicit capabilities, tenant-aware authorization, role ranking, grants and denials, and a controlled owner path for exceptional operations. Sensitive areas such as team management, earnings, payments, and cross-tenant administration received dedicated authorization coverage. This moved security out of assumptions spread across the UI and into rules enforced at the server boundary.</p><h2>Redesigning the frontend, not just repainting it</h2><p>The frontend needed the same treatment as the backend. I redesigned major customer and administrative surfaces while reorganizing the code around product features and shared foundations. I introduced TanStack Query for server state, replaced chains of effects and local loading flags with reusable query hooks, and established consistent loading, error, empty, offline, and success states. Failed requests stopped looking like empty data, and screens stopped spinning indefinitely when an integration or API call failed.</p><p>I also added Vitest, stricter TypeScript boundaries, and runtime validation at service edges. Along the way I fixed accumulated bugs, removed duplicated data flows, and made the interface easier for contractors and the internal team to understand. The visible redesign mattered, but the larger improvement was that the UI finally had predictable behavior underneath it.</p><h2>Building for operations and failure</h2><p>A production platform is not reliable just because the happy path works. I added structured, redacted error capture, an append-only audit trail, provider health visibility, and a unified operational event stream so failures could be investigated without exposing customer data.</p><p>I hardened Stripe processing with signature validation, event deduplication, and idempotent subscription creation. I also improved scheduled-job tracking and fixed integration paths where delivery state and alert data could silently drift from reality. These changes made failures visible and gave the team evidence to diagnose them.</p><p>Preview and pull-request environments became part of the engineering workflow, with migration checks and architecture guardrails that made large changes safer to review before release.</p><h2>Removing a deployment bottleneck</h2><p>As the server-owned API grew, Vercel was building 266 separate function entry points. On the same branch, preview builds took between 9 minutes 45 seconds and 11 minutes 37 seconds.</p><p>I consolidated those entry points to 11 functions and protected the change with generated route manifests, route snapshots, and route-equivalence tests. The measured preview build fell to 1 minute 35 seconds, an 84 to 86 percent reduction. This work was implemented and validated in preview, so the number describes build time, not customer request latency.</p><h2>The result</h2><p>The result was not one headline feature. It was a product that had moved from implicit, scattered behavior to explicit boundaries:</p><ul><li><p>Application data now flows through an authorized server layer.</p></li><li><p>Tenant and role permissions are enforced as capabilities instead of UI assumptions.</p></li><li><p>The frontend uses consistent data-fetching and failure-state patterns.</p></li><li><p>Payments, scheduled jobs, and integrations are observable and safer to retry.</p></li><li><p>Large architectural changes can be exercised in isolated preview environments.</p></li><li><p>Preview deployment feedback became dramatically faster.</p></li></ul><p>The hardest part was improving the foundation without freezing product work. This project taught me how to replace risky architecture incrementally, preserve working customer flows, and leave a growing team with a system that is easier to understand than the one I inherited.</p>

More Projects

Walker & Homes Photography

Infomaniak
Next.js
PostgreSQL
Prisma
React
+5

Yeet or Keep

React Native
Expo
Next.js
Shadcn UI
Tailwind CSS
+1

Hexis

Java
Rust
C
Lua
TypeScript
+3
View All Projects