IHSS Connect

Led the rebuild of a caregiver-matching platform from a legacy MongoDB app into a NestJS and Next.js product, and moved 12,000+ users and 30,000+ messages across with their logins intact.

IHSS Connect messages screen, with a conversation list beside a chat showing a care request.
IHSS Connect account settings, showing a caregiver profile with a completion progress bar.
IHSS Connect public website, with cards for finding caregivers, real-time messaging and a job board.
Personal details are hidden in these screens.

The problem#

IHSS Connect is where California families on the In-Home Supportive Services program find caregivers, and where caregivers find work. People search by distance and need, message each other, post and apply for jobs, and verify themselves with a background check.

The original app had grown on MongoDB for years. It held thousands of active accounts and every conversation those people had ever had, so it couldn’t simply be switched off and replaced. It needed a new foundation, and everyone’s history had to come with it.

12,000+
user accounts moved
30,000+
messages preserved
14,000+
conversations
13
languages served
Approximate figures from the production migration.

What I did#

  • Owned the architecture and led the team from the first schema to the production cutover.
  • Designed the NestJS backend: a modular API on PostgreSQL, with PostGIS for distance search and Redis-backed queues for everything that shouldn’t block a request.
  • Built the migration tooling that mapped a decade of MongoDB documents onto a relational schema, kept legacy password hashes so nobody had to reset their login, and could be re-run safely until the numbers were right.
  • Set the engineering standards: a typed API client generated from the OpenAPI spec, a large backend test suite plus end-to-end coverage, and AI-assisted code review.

The platform#

Select any part below to see its role.

Families & caregivers Search by distance, tasks, medical needs, transport, certificates and availability. 13 languages, including right-to-left Farsi and Arabic.

Marketing site A WordPress site that pulls sponsor placements from the API through a small plugin, with impression and click tracking.

Next.js app App Router with a typed client generated from the OpenAPI spec, so the frontend can’t drift from the API.

NestJS API A modular API covering auth, caregivers, recipients, jobs, messaging, verification, payments, sponsorship, admin and audit.

Real-time messaging Socket.IO for live chat, typing and read receipts. Away messages and email digests are handled by queued jobs, not in the request.

Queues & cron BullMQ on Redis for SMS and email. Locked cron jobs expire old listings, send saved-search alerts and nudge unverified caregivers.

PostgreSQL + PostGIS A relational schema evolved through versioned migrations. Distance search runs in the database and degrades gracefully where PostGIS isn’t available.

Background checks & payments Checks run through a verification provider’s hosted flow, paid with Stripe, confirmed by webhooks, with expiry reminders.

SMS, email & storage Managed providers for SMS and one-time codes, transactional email, and object storage for every uploaded image.

Legacy MongoDB app The source of truth until cutover. Read, inspected and mapped by the migration tool, then retired.

Moving everyone across#

The migration was treated as a product in its own right rather than a script run once at the end.

  • Inventory first. A planning command inspected the live MongoDB collections and produced a report of what existed, what mapped cleanly and what didn’t, before a single row was written.
  • Lookups before people. Task types, medical issues and certificates were aligned first, with aliases for the spellings that had accumulated over the years, so profiles landed on clean data.
  • Dry run, then resume. Every phase ran in dry-run mode, and a real run could stop and pick up where it left off. Records that couldn’t be matched were reported, not silently dropped.
  • Nothing lost on the way. ObjectIds were mapped to new IDs so relationships held. Password hashes were carried over unchanged. Every image was copied to object storage and relinked.

The existing service stayed in place while the new platform was built and rehearsed against real data. The final switch was a planned cutover, with the old app kept available as the fallback.

How it shipped#

  1. Architect

    Relational schema designed from the legacy documents, with PostGIS for search and queues for side effects.

  2. Rebuild

    API and app built in parallel against a shared OpenAPI contract, with tests and AI-assisted review on every change.

  3. Rehearse

    Migration run repeatedly against production data in dry-run mode until the counts and warnings were understood.

  4. Cut over

    Accounts, conversations and images moved in one planned window. Users kept their logins and their history.

Working on something similar? Get in touch