Skip to content
All projects
ShippedJul 2026 – Oct 2026

Bubbas Closet

A subscription clothing rental for kids: members build a box from a members-only closet, choose the monthly plan that covers it, wear the pieces for a month, then send them back and swap.

The mybubbascloset.ca homepage: "Rent a rotating children's wardrobe, monthly."

My role

Solo build under contract for a Kelowna client. I owned the data model, billing, member portal, admin tools, and deployment.

Stack

  • Next.js 16 (App Router)
  • TypeScript
  • Tailwind CSS v4
  • Stripe
  • Firebase Auth
  • Cloud Firestore
  • Firebase Storage
  • Firebase App Hosting
  • Cloud Scheduler
  • GitHub Actions
  • Resend

Architecture

Member requests
  1. Browser signs in with Firebase Auth
  2. ID token exchanged for an httpOnly session cookie
  3. Next.js route handlers verify the cookie with firebase-admin
  4. Firestore
Billing
  1. Stripe Checkout and Billing Portal
  2. Stripe webhooks
  3. /api/stripe/webhook
  4. Subscriptions in Firestore
Scheduled jobs
  1. Cloud Scheduler
  2. Release expired holds (every 5 min) and return reminders (daily)
  3. Firestore and Resend email

Hardest problem: Two members can never rent the same garment

Inventory is serialized. A product is a style; a unit is one physical garment with its own status: available, reserved, out, cleaning, or retired. Members browse styles, but every piece they add has to claim a specific unit.

Adding a piece runs a Firestore transaction that reads the unit and the member's live holds together, checks that the unit is still available and the box is under the plan's item limit, then reserves the unit with a 20-minute hold. If another member wins the race, it moves on to the next available unit in that size. The tier limit is enforced inside that transaction, never in the browser.

Holds expire, so a Cloud Scheduler job sweeps them every five minutes. The first version only looked at hold documents, so a garment whose hold disappeared some other way stayed reserved forever. The sweep now also checks reserved units with a missing or expired hold and returns them to the closet.

Billing follows the same rule of keeping state on the server. Upgrades apply immediately with proration, downgrades are scheduled with a Stripe subscription schedule for the next cycle, and confirming a box creates one monthly order keyed to the Stripe billing period.

What I'd do next

Tests
CI runs lint, type-checking, and a production build on every push, but there are no automated tests yet. First would be unit tests for the hold, tier-limit, and shipping rules, then an end-to-end test of browse, build a box, and subscribe against Stripe test mode.
Metrics
Track box-to-subscription conversion and return turnaround so the client can tune hold length and stock levels with data.
Scale
Queries use a single equality filter and refine in memory, which avoids composite indexes at the current catalogue size. A larger catalogue would need composite indexes and paginated queries.

Links

Next case studyLocal Events & Specials App