True Baby Cost - Data Architecture Design

Version: 1.0
Date: 2026-02-18
Status: Approved Design


Table of Contents

  1. Executive Summary
  2. Current State Analysis
  3. Core Schema Design
  4. Category-Specific Schemas
  5. Sample Data Files
  6. Migration Plan
  7. Build System Recommendation
  8. API Design (Future)

Executive Summary

Problem

Product data is currently hardcoded across 7+ HTML files (46KB-107KB each), making:

  • Updates tedious and error-prone
  • Search/filtering features difficult to implement
  • Consistency hard to maintain
  • SEO and content freshness challenging

Solution

Centralized JSON data layer with:

  • Unified base schema shared across all product types
  • Category-specific extensions for unique attributes
  • Build-time generation of static HTML (keeps hosting simple)
  • Future-ready for API endpoints and dynamic features

Key Benefits

BenefitCurrentAfter
Add new productEdit 100+ lines HTMLAdd 1 JSON object
Update priceFind/replace in HTMLEdit 1 field
Search all productsNot possibleQuery JSON
A/B test contentCopy entire fileSwap data source
Analytics per productManual trackingStructured IDs

Current State Analysis

Existing Data Sources

FileFormatProductsSize
data/strollers.json✅ JSON35+ strollers113KB
diapers.html❌ Hardcoded8 brands46KB
formula.html❌ Hardcoded15 brands59KB
wipes.html❌ Hardcoded12 brands49KB
bottles.html❌ Hardcoded15+ systems107KB
breast-pumps.html❌ Hardcoded12 pumps73KB
checklist.html❌ Hardcoded100+ items83KB

Strollers.json Structure (Reference)

The existing strollers schema provides a good foundation: ```json { “id”: “uppababy-cruz-v3”, “brand”: “UPPAbaby”, “model”: “Cruz V3”, “basePrice”: 749.99, “category”: “luxury”, “type”: “full-size”, “weightLbs”: 23.2, “ageRange”: “Birth to 50 lbs”, “description”: ”…”, “manufacturerUrl”: “https://…”, “amazonUrl”: “https://…”, “rating”: 4.7, “highlights”: [”…”, ”…”], “accessories”: […] } ```

Key Observations

  1. Strollers already use JSON — we extend this pattern
  2. Products have different cost models:
    • One-time purchase (strollers, bottles)
    • Consumables (diapers, wipes, formula)
    • Hybrid (breast pumps: device + consumable parts)
  3. Tier categorization varies:
    • Strollers: luxury/mid-range/budget
    • Diapers: per-diaper price tiers
    • Pumps: manual/standard/portable/hospital

Core Schema Design

TypeScript Interfaces

```typescript // ═══════════════════════════════════════════════════════════════════ // CORE TYPES - Shared across all product categories // ═══════════════════════════════════════════════════════════════════

/**

  • UsageRange - Tracks when a product is appropriate for use
  • Essential for timeline visualization and product transitions / interface UsageRange { /* Age when product can start being used: “newborn”, “3m”, “6m”, “12m” */ ageStart?: string;

/** Age when product is typically outgrown: “4m”, “12m”, “3y” */ ageEnd?: string;

/** Minimum weight in lbs (e.g., car seat minimums) */ weightMinLbs?: number;

/** Maximum weight in lbs (e.g., stroller 50lb limit) */ weightMaxLbs?: number;

/** Additional context: “until rolling”, “or sitting unassisted” */ notes?: string; }

/**

  • Price tier classification - standardized across categories */ type PriceTier = ‘budget’ | ‘mid’ | ‘premium’ | ‘luxury’;

/**

  • Links to purchase/learn more */ interface ProductLinks { manufacturerUrl?: string; amazonUrl?: string; targetUrl?: string; walmartUrl?: string; costcoUrl?: string; reviewUrl?: string; }

/**

  • SEO and metadata */ interface ProductMeta { lastUpdated: string; priceSource?: string; badges?: Array<‘wirecutter-pick’ | ‘best-value’ | ‘editor-choice’ | ‘insurance-covered’>; relatedIds?: string[]; }

/**

  • Base product interface - ALL products extend this */ interface BaseProduct { id: string; brand: string; name: string; tier: PriceTier; description: string; highlights: string[]; usageRange?: UsageRange; links: ProductLinks; meta: ProductMeta; rating?: number; image?: string; icon?: string; }

// ═══════════════════════════════════════════════════════════════════ // COST MODELS - Different products have different cost structures // ═══════════════════════════════════════════════════════════════════

/**

  • One-time purchase cost model
  • Used by: Strollers, bottles, car seats, furniture */ interface OneTimeCost { costModel: ‘one-time’; basePrice: number; accessories?: Array<{ id: string; name: string; price: number; included: boolean; category: string; description?: string; links?: ProductLinks; }>; }

/**

  • Consumable/recurring cost model
  • Used by: Diapers, formula, wipes */ interface ConsumableCost { costModel: ‘consumable’; unit: string; pricePerUnit: number; unitsPerDay?: number; projectedCosts?: { monthly?: number; sixMonth?: number; yearly?: number; total?: number; }; typicalDurationMonths?: number; }

/**

  • Hybrid cost model (device + consumables)
  • Used by: Breast pumps, bottle systems with replacement parts */ interface HybridCost { costModel: ‘hybrid’; devicePrice: number; recurringCosts: Array<{ name: string; pricePerUnit: number; unit: string; frequencyMonths?: number; monthlyEstimate?: number; }>; projectedCosts: { month1?: number; sixMonths: number; twelveMonths: number; eighteenMonths?: number; }; insuranceNotes?: string; }

// ═══════════════════════════════════════════════════════════════════ // PRODUCT CATEGORIES - Specific types extending base // ═══════════════════════════════════════════════════════════════════

type StrollerType = ‘full-size’ | ‘lightweight’ | ‘jogger’ | ‘double’ | ‘travel-system’;

interface Stroller extends BaseProduct, OneTimeCost { category: ‘stroller’; type: StrollerType; weightLbs: number; foldedDimensions?: string; specs?: { seatWidth?: string; canopyUPF?: number; basketCapacityLbs?: number; handlebarHeight?: string; }; }

type DiaperType = ‘disposable’ | ‘cloth’ | ‘hybrid’;

interface Diaper extends BaseProduct, ConsumableCost { category: ‘diaper’; type: DiaperType; distributor?: string; features?: string[]; sizes?: string[]; }

type FormulaType = ‘standard’ | ‘sensitive’ | ‘organic’ | ‘specialty’ | ‘ready-to-feed’;

interface Formula extends BaseProduct, ConsumableCost { category: ‘formula’; type: FormulaType; manufacturer?: string; features?: string[]; sizeOptions?: Array<{ size: string; price: number; pricePerOz: number; }>; }

type WipeType = ‘disposable’ | ‘reusable’;

interface Wipe extends BaseProduct, ConsumableCost { category: ‘wipe’; type: WipeType; packSize?: number; features?: string[]; }

type BottleMaterial = ‘plastic’ | ‘glass’ | ‘silicone’; type BottleSize = ‘4oz’ | ‘5oz’ | ‘8oz’ | ‘9oz+’;

interface BottleSystem extends BaseProduct, HybridCost { category: ‘bottle’; material: BottleMaterial; sizes: BottleSize[]; features?: string[]; starterSetIncludes?: string[]; }

type PumpType = ‘manual’ | ‘standard’ | ‘portable’ | ‘hospital’;

interface BreastPump extends BaseProduct, HybridCost { category: ‘breast-pump’; type: PumpType; manufacturer?: string; features?: string[]; insuranceCoverage?: ‘commonly-covered’ | ‘sometimes-covered’ | ‘rarely-covered’; partsSchedule?: { flanges: { intervalMonths: number; cost: number }; valves: { intervalMonths: number; cost: number }; tubing?: { intervalMonths: number; cost: number }; }; }

// ═══════════════════════════════════════════════════════════════════ // COLLECTION TYPES - For data files // ═══════════════════════════════════════════════════════════════════

interface ProductCollection { lastUpdated: string; version: string; category: string; products: T[]; }

// ═══════════════════════════════════════════════════════════════════ // CHECKLIST ITEMS - For baby prep checklist // ═══════════════════════════════════════════════════════════════════

type ChecklistPriority = ‘essential’ | ‘nice-to-have’;

interface PriceRange { min: number; max: number; }

interface ChecklistItem extends BaseProduct { category: ‘checklist’; parentCategoryId: string; priority: ChecklistPriority; prices: { budget: PriceRange; mid: PriceRange; premium?: PriceRange; }; defaultQuantity: number; maxQuantity: number; tags?: string[]; dependsOn?: string[]; } ```


Category-Specific Schemas

File Structure

``` stroller-app/ ├── data/ │ ├── strollers.json # ✅ Already exists │ ├── diapers.json # 🆕 Extract from HTML │ ├── formula.json # 🆕 Extract from HTML │ ├── wipes.json # 🆕 Extract from HTML │ ├── bottles.json # 🆕 Extract from HTML │ ├── breast-pumps.json # 🆕 Extract from HTML │ ├── checklist.json # 🆕 For baby prep checklist │ └── meta/ │ ├── categories.json # Category metadata │ └── schema-version.json # Schema version tracking ```


Sample Data Files

diapers.json

```json { “lastUpdated”: “2026-02-18”, “version”: “1.0.0”, “category”: “diapers”, “calculatorDefaults”: { “diapersPerDay”: 7, “pottyAgeMonths”: 28 }, “products”: [ { “id”: “parents-choice-disposable”, “brand”: “Parent’s Choice”, “name”: “Parent’s Choice Diapers”, “tier”: “budget”, “description”: “Walmart’s store brand offering 12-hour protection at the lowest price per diaper.”, “highlights”: [“Best Value”, “12-hr Protection”, “Wetness Indicator”], “usageRange”: { “ageStart”: “newborn”, “ageEnd”: “3y”, “weightMinLbs”: 6, “weightMaxLbs”: 35, “notes”: “Until potty trained” }, “links”: { “amazonUrl”: “https://www.amazon.com/s?k=parent%27s+choice+diapers&tag=truebabycost-20”, “walmartUrl”: “https://www.walmart.com/c/kp/parents-choice-diapers” }, “meta”: { “lastUpdated”: “2026-02-18”, “badges”: [“best-value”] }, “icon”: ”🧷”, “category”: “diaper”, “type”: “disposable”, “costModel”: “consumable”, “unit”: “diaper”, “pricePerUnit”: 0.15, “distributor”: “Walmart”, “features”: [“12-hr Protection”, “Wetness Indicator”], “sizes”: [“N”, “1”, “2”, “3”, “4”, “5”, “6”], “projectedCosts”: { “monthly”: 32, “yearly”: 383, “total”: 1149 }, “typicalDurationMonths”: 28 }, { “id”: “pampers-swaddlers”, “brand”: “Pampers”, “name”: “Pampers Swaddlers”, “tier”: “mid”, “description”: “America’s #1 choice with wetness indicator and umbilical notch.”, “highlights”: [“Hospital Recommended”, “Wetness Indicator”, “Umbilical Notch”], “links”: { “amazonUrl”: “https://www.amazon.com/s?k=pampers+swaddlers&tag=truebabycost-20” }, “meta”: { “lastUpdated”: “2026-02-18”, “badges”: [“wirecutter-pick”] }, “rating”: 4.7, “icon”: ”🧷”, “category”: “diaper”, “type”: “disposable”, “costModel”: “consumable”, “unit”: “diaper”, “pricePerUnit”: 0.35, “projectedCosts”: { “monthly”: 74, “yearly”: 893, “total”: 2679 } }, { “id”: “coterie-disposable”, “brand”: “Coterie”, “name”: “Coterie Diapers”, “tier”: “luxury”, “description”: “Ultra-premium with hospital-grade absorbency.”, “highlights”: [“Hospital-Grade”, “Softest Materials”, “No Blowouts Guarantee”], “links”: { “amazonUrl”: “https://www.amazon.com/s?k=coterie+diapers&tag=truebabycost-20” }, “meta”: { “lastUpdated”: “2026-02-18” }, “rating”: 4.8, “icon”: ”🧷”, “category”: “diaper”, “type”: “disposable”, “costModel”: “consumable”, “unit”: “diaper”, “pricePerUnit”: 0.66, “projectedCosts”: { “monthly”: 140, “yearly”: 1685, “total”: 5056 } } ] } ```

breast-pumps.json

```json { “lastUpdated”: “2026-02-18”, “version”: “1.0.0”, “category”: “breast-pumps”, “calculatorDefaults”: { “pumpingDurationMonths”: 12, “storageBagsMonthlyCost”: 25 }, “products”: [ { “id”: “spectra-s2-plus”, “brand”: “Spectra”, “name”: “Spectra S2 Plus”, “tier”: “mid”, “description”: “Hospital-strength double electric. Gold standard for exclusive pumping.”, “highlights”: [”⭐ Wirecutter Pick”, “Closed System”, “Hospital Strength”, “Quiet”], “usageRange”: { “ageStart”: “newborn”, “ageEnd”: “24m” }, “links”: { “amazonUrl”: “https://www.amazon.com/s?k=spectra+s2+plus&tag=truebabycost-20” }, “meta”: { “lastUpdated”: “2026-02-18”, “badges”: [“wirecutter-pick”, “insurance-covered”] }, “rating”: 4.7, “icon”: ”🤱”, “category”: “breast-pump”, “type”: “standard”, “manufacturer”: “Spectra Baby USA”, “features”: [“Closed System”, “Hospital Strength”, “Timer”], “insuranceCoverage”: “commonly-covered”, “costModel”: “hybrid”, “devicePrice”: 180, “recurringCosts”: [ { “name”: “Replacement Flanges”, “pricePerUnit”: 25, “unit”: “set”, “frequencyMonths”: 3, “monthlyEstimate”: 8 }, { “name”: “Duckbill Valves”, “pricePerUnit”: 10, “unit”: “pack”, “frequencyMonths”: 2, “monthlyEstimate”: 5 }, { “name”: “Tubing”, “pricePerUnit”: 12, “unit”: “set”, “frequencyMonths”: 6, “monthlyEstimate”: 2 } ], “projectedCosts”: { “month1”: 280, “sixMonths”: 520, “twelveMonths”: 640 }, “insuranceNotes”: “Most ACA-compliant plans cover this pump.” }, { “id”: “momcozy-s12-pro”, “brand”: “Momcozy”, “name”: “Momcozy S12 Pro”, “tier”: “budget”, “description”: “Affordable wearable pump. Popular alternative to premium wearables.”, “highlights”: [“Budget Wearable”, “Hands-Free”, “Quiet”], “links”: { “amazonUrl”: “https://www.amazon.com/s?k=momcozy+s12+pro&tag=truebabycost-20” }, “meta”: { “lastUpdated”: “2026-02-18”, “badges”: [“best-value”] }, “rating”: 4.4, “icon”: ”🤱”, “category”: “breast-pump”, “type”: “portable”, “insuranceCoverage”: “rarely-covered”, “costModel”: “hybrid”, “devicePrice”: 70, “recurringCosts”: [ { “name”: “Replacement Parts Set”, “pricePerUnit”: 20, “unit”: “set”, “frequencyMonths”: 3, “monthlyEstimate”: 7 } ], “projectedCosts”: { “month1”: 170, “sixMonths”: 280, “twelveMonths”: 430 } } ] } ```


Migration Plan

Phase 1: Data Extraction (1-2 days)

  1. Create JSON files from HTML (manual or scripted)
  2. Validate against TypeScript schema
  3. Update strollers.json to new format

Phase 2: Template Creation (2-3 days)

  1. Create shared Handlebars partials (nav, footer, cards)
  2. Create category-specific templates
  3. Test template output matches current HTML

Phase 3: Build System Setup (1-2 days)

  1. Set up Node.js build script
  2. Configure GitHub Actions CI/CD
  3. Test generated output

Phase 4: Cutover (1 day)

  1. Generate all HTML from data
  2. Visual diff testing
  3. Deploy

Migration Checklist

  • Extract diapers.json
  • Extract formula.json
  • Extract wipes.json
  • Extract bottles.json
  • Extract breast-pumps.json
  • Update strollers.json schema
  • Create checklist.json
  • Build script working
  • CI/CD configured
  • Deploy

Build System Recommendation

Recommendation: Static Generation with Node.js

OptionProsConsVerdict
Static Gen (Node.js)Simple hosting, fast, SEO-perfectNo real-time dataBest fit
Next.js SSGGreat DX, React ecosystemOverkill for staticConsider for v2
Runtime JSDynamic featuresSlower, client-dependent

```javascript // build.js const fs = require(‘fs’); const Handlebars = require(‘handlebars’);

const diapers = require(‘./data/diapers.json’); const template = Handlebars.compile( fs.readFileSync(‘./templates/diapers.hbs’, ‘utf8’) );

const html = template({ products: diapers.products, lastUpdated: diapers.lastUpdated });

fs.writeFileSync(‘./dist/diapers.html’, html); ```

Directory Structure

``` stroller-app/ ├── data/ # JSON data files ├── templates/ # Handlebars templates │ ├── layouts/ │ ├── partials/ │ └── *.hbs ├── static/ # CSS, images ├── dist/ # Generated HTML (gitignored) ├── scripts/ │ ├── build.js │ └── validate-data.ts └── types/ └── schema.ts ```


API Design (Future)

When ready for API endpoints:

Search Endpoint

```typescript // GET /api/search?q=pampers&category=diaper&tier=mid export async function onRequest({ request }) { const url = new URL(request.url); const query = url.searchParams.get(‘q’)?.toLowerCase();

let results = getAllProducts(); if (query) { results = results.filter(p p.name.toLowerCase().includes(query) ); }

return Response.json({ results }); } ```

Compare Endpoint

```typescript // GET /api/compare?ids=pampers-swaddlers,huggies-little-snugglers export async function onRequest({ request }) { const ids = new URL(request.url).searchParams.get(‘ids’)?.split(’,’); const products = ids.map(findProductById).filter(Boolean);

return Response.json({ products, cheapest: products.reduce((a, b) getAnnualCost(a) < getAnnualCost(b) ? a : b ) }); } ```


Summary

What We’re Building

  1. Centralized JSON data - Single source of truth
  2. Type-safe schema - TypeScript prevents errors
  3. Static generation - Fast, SEO-optimized
  4. Future-ready - Easy to add APIs

Next Steps

  1. Start with diapers.json as pilot
  2. Create Handlebars template
  3. Validate build output
  4. Migrate remaining categories

Success Metrics

  • ✅ All products in JSON
  • ✅ < 5 min to add a new product
  • ✅ Single-field price updates
  • ✅ TypeScript catches data errors

Document created: 2026-02-18 Author: True Baby Cost Team