Skip to main contentSkip to footer
    Back to Case Studies
    Pet BreederDirectory, petbreederdirectory.com
    Web PlatformProgrammatic SEOIn-House Product

    Pet Breeder Directory

    A breeder, shelter and breed directory that runs on hardware we own: 249,614 aggregated listings, 452 breeds across seven species, and roughly 14.1 million breed-by-city pages served from a single self-hosted stack.

    Project Preview

    Live website preview - petbreederdirectory.com

    249,614
    Breeder listings aggregated from public sources
    452
    Breeds across 7 species
    ~14.1M
    Programmatic breed-by-city pages
    7,143
    Shelter and rescue organizations

    Search that scales

    Meilisearch handles full-text, faceted and geo-radius breeder search over two indexes. Breed search runs on Postgres pg_trgm so a misspelled breed still finds its page.

    Geospatial map

    PostGIS geography columns with spatial indexes feed bounding-box queries into a Mapbox GL map that renders only in the browser, never in SSR.

    ~14.1M ISR pages

    One dynamic route, 452 breeds times 31,190 US cities, rendered on demand with incremental static regeneration and cached at the Cloudflare edge.

    Auth we own

    Custom JWT sessions, Google OAuth with PKCE, TOTP two-factor and recovery codes. No auth vendor, no row-level security: authorization is explicit role checks in app code.

    Lifecycle email engine

    Fourteen declarative drip sequences over React Email templates, a 24-hour cooldown per recipient, inbound-reply handling, and cancellation on real user events.

    Stripe billing

    Breeder, buyer and shelter subscriptions, one-time boosts, a card-first 14-day trial, dunning retries, grace periods and jurisdiction-aware refund windows.

    The problem

    Finding a breeder is a research project. The information exists, but it is scattered across kennel-club registries, national breed clubs, shelter databases and thousands of individual breeder sites, in a dozen languages and formats. A buyer searching for a specific breed near a specific town gets either a marketplace that lists puppies like appliances, or a forum thread from 2014.

    Pet Breeder Directory is a Frenchy Digital product, founded by Chris Machetto. The brief we set ourselves was to aggregate that public data into one searchable place, give breeders and shelters a profile they can claim and manage, and do it on infrastructure cheap enough to survive the years it takes a directory to earn its traffic. That last constraint decided most of the architecture: no per-seat SaaS bills, no managed database that charges by the row, one server we control.

    What the directory is, and is not

    • Aggregated, not vetted: The 249,614 listings are bulk-imported from public sources. A small number are PBD-verified after human review, and buyers are told plainly that they must do their own due diligence. The product never implies breeders are screened as a class.
    • Majority European by coverage: The largest country counts are the Czech Republic, France, the United States, the United Kingdom, Sweden and Italy. US and Canada are the go-to-market focus, not the footprint.
    • Seven species, not just dogs: 228 dog breeds, 80 cat, 57 bird, 30 reptile, 25 small animal, 20 fish and 12 horse breeds, each with an encyclopedia page.
    • Early on the paid side: Billing, reviews, litters and alerts all ship as working code. This page reports what the code does, not traction it has not earned yet.

    What we built

    For buyers

    Breeder search by breed, species, location, kennel-club membership and geo-radius, with a clustered map view
    Breed encyclopedia for 452 breeds, side-by-side breed comparison and a lifestyle quiz
    Shelter and rescue directory of 7,143 organizations, with Stripe Connect so donations reach them directly
    Membership-only contact reveals, with a 14-day card-first trial and grandfathered quotas for earlier accounts
    Community breeder submissions with an admin moderation queue, and a reported-breeder blacklist
    Concierge research and matching services for buyers who want it done for them

    For breeders and shelters

    Profile claiming with a multi-step flow, Stripe Identity document-and-selfie verification, and free self-serve website confirmation
    Animal, litter and pedigree management, with animal and litter registration as paid one-time products
    Three breeder plans, from city-level to national visibility, plus per-seat team licences and paid listing boosts
    Analytics dashboard with daily views, search impressions and contact clicks, and a weekly digest for higher tiers
    Embeddable profile badges and status widgets, edge-cached and rate-limited at Cloudflare
    A support desk whose tickets can trigger an automated diagnosis run against the codebase

    How it works

    One box, four containers

    Postgres 16 + PostGIS via Drizzle

    The application talks to Postgres directly through Drizzle ORM. There is no Supabase client and no row-level security; every query is an explicit select with a role check around it, and migrations are generated SQL applied by hand rather than on deploy. PostGIS geography columns carry breeder locations and power the map's bounding-box queries.

    Meilisearch for breeders, pg_trgm for breeds

    Breeder and organization search live in two Meilisearch indexes with facets, boost scoring and geo-radius. Breed lookups deliberately do not: a trigram similarity query in Postgres handles misspellings without a second index to keep in sync. The last full resync indexed all 249,614 breeder documents.

    MinIO behind a Cloudflare hostname

    Animal photos and breeder images upload straight from the browser to S3-compatible MinIO through presigned URLs, rate-limited per IP, and are served from a public bucket on an images subdomain fronted by Cloudflare.

    Coolify, Docker and a local build

    Coolify orchestrates the containers on the VPS. The deploy script builds the Next.js output on a workstation, syncs it to the server, installs native dependencies there so the image matches the target architecture, then packages, deploys and purges the edge cache. There is no CI runner in the loop.

    Programmatic SEO at fourteen million pages

    A single dynamic route under the app root resolves a slug into a breed and a US city, 452 by 31,190, and renders a breed-in-city page with incremental static regeneration. Nothing is pre-built; a page exists the first time someone or something asks for it, and Cloudflare cache rules hold anonymous HTML for the breed, breeder, blog and SEO surfaces at the edge. The site also publishes llms.txt files so answer engines can read the catalogue directly, and the 303 published blog posts feed the same cache.

    Two Next.js details cost real time and are now rules in the repo. A root loading boundary flushes a 200 shell before a missing page can return 404, which soft-404s every bad URL site-wide, so there is none. And a fragment cannot be the root of a streaming boundary, because React needs a real DOM node to complete it.

    Authentication without a vendor

    Sessions are signed JWTs in a cookie, issued by the app. Google sign-in uses OAuth with PKCE and a CSRF state parameter. Two-factor is TOTP with recovery codes, with SMS as a fallback channel. Rate limiting sits in front of the auth routes. The trade-off is deliberate: owning auth means owning its bugs, but it also means the directory has no per-user cost and no third party in the session path.

    Email as a state machine

    The sequence engine is a registry of fourteen declarative drips, code-verified by count in the registry, covering buyer onboarding, free-to-premium upgrade, breeder onboarding and nudges, cold outreach and free-claim invitations, win-back, cancellation, abandoned signup and referral journeys. Each step is a React Email template. The engine enforces a 24-hour cooldown per recipient across every sequence, cancels an upgrade drip the moment a Stripe webhook reports a subscription, and pauses it while a human reply thread is open. Inbound mail arrives on a webhook that honours stop words and forwards a copy to a person.

    Cold outreach to breeders goes out through Smartlead rather than the transactional domain, with warm-up, daily caps and automatic blocking if mailbox reputation drops. Reported honestly: as of the last documented audit the sequence cron was not yet scheduled in production, and the backlog has to be expired before it is switched on, or months-old sequences would all fire at once.

    Problems worth talking about

    Two kinds of verified

    A human-reviewed PBD-verified badge and a free, self-serve domain confirmation are different claims, and the copy must never merge them. A unit test now fails the build if any string calls the domain tier "verified".

    SSRF on a shared host

    The app fetches user-supplied breeder URLs to confirm domain ownership, and it shares a machine with Postgres, Meilisearch, MinIO and the orchestrator. Every user-triggered fetch now goes through a layer that resolves DNS through explicit nameservers, pins the classified IP for the connection, caps the response and bounds the redirect chain under one deadline, with a kill switch in the environment.

    A search index that only grows

    The sync script upserts and never deletes, so rows purged by SQL stayed in Meilisearch as ghosts whose profile pages 404. The 2026 audit found 1,837 of them. Reconciliation is now a documented id diff, not a re-sync.

    Edge cache versus content hashes

    A deploy changed every chunk filename while Cloudflare kept serving yesterday's HTML. Server checks all looked healthy; no browser could boot the app. Cache purge is now a hard, asserted step of the deploy script.

    Where it stands

    Reported honestly

    Every number on this page comes from the production database audit recorded in the repository or from counting the code: listings, breeds, species, organizations, cities, sequences. The directory is live and indexed. We do not publish subscriber counts, revenue or review volumes here, because those figures are small and a case study is not the place to dress them up. This page will be updated as they change.

    The directory is aggregated, not vetted. Never write copy implying breeders are screened as a class.

    Pet Breeder Directory engineering notes

    What This Project Does Well

    The whole stack, Postgres 16 + PostGIS, Meilisearch, MinIO and the Next.js app, runs as containers on one VPS under Coolify, behind Cloudflare DNS and CDN
    Two verification tiers kept honest: a human-reviewed PBD-verified badge and a separate self-serve website confirmation, with a test that fails the build if copy conflates them
    Every user-triggered outbound request goes through a safe-fetch layer that pins the resolved IP, caps the body and bounds the redirect chain, because the app shares a host with its databases
    A GDPR removal pipeline, domain re-verification, boost expiry and subscription reversion all run as scheduled jobs against authenticated cron routes
    Cold breeder outreach warmed through Smartlead with mailbox-reputation monitoring, so the transactional Resend domain never carries the risk
    Deploys build locally, sync to the server, and purge the Cloudflare cache as a hard step, after stale edge HTML once pointed every visitor at chunks that no longer existed
    Sentry instrumentation across server, edge and client, with session cookies stripped before an event leaves the box

    Technologies Used

    Next.js 15 (App Router, ISR), React 19, TypeScript strict, Tailwind CSS 4, Drizzle ORM, PostgreSQL 16 with PostGIS and pg_trgm, Meilisearch, MinIO (S3-compatible), custom JWT sessions (JOSE), Google OAuth, TOTP MFA, Resend and React Email, Smartlead, Stripe (Checkout, Identity, Connect, webhooks), Mapbox GL JS, PostHog, Sentry, Zod, Vitest, Playwright, Bun for development, Node 22 in production, Docker, Coolify on a VPS, Cloudflare DNS, CDN and rate limiting

    Ready to Build Something Similar?

    Let's discuss how we can help transform your business with a custom digital solution.