True Baby Cost - Data Architecture Design
Version: 1.0
Date: 2026-02-18
Status: Approved Design
Table of Contents
- Executive Summary
- Current State Analysis
- Core Schema Design
- Category-Specific Schemas
- Sample Data Files
- Migration Plan
- Build System Recommendation
- 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
| Benefit | Current | After |
|---|---|---|
| Add new product | Edit 100+ lines HTML | Add 1 JSON object |
| Update price | Find/replace in HTML | Edit 1 field |
| Search all products | Not possible | Query JSON |
| A/B test content | Copy entire file | Swap data source |
| Analytics per product | Manual tracking | Structured IDs |
Current State Analysis
Existing Data Sources
| File | Format | Products | Size |
|---|---|---|---|
| data/strollers.json | ✅ JSON | 35+ strollers | 113KB |
| diapers.html | ❌ Hardcoded | 8 brands | 46KB |
| formula.html | ❌ Hardcoded | 15 brands | 59KB |
| wipes.html | ❌ Hardcoded | 12 brands | 49KB |
| bottles.html | ❌ Hardcoded | 15+ systems | 107KB |
| breast-pumps.html | ❌ Hardcoded | 12 pumps | 73KB |
| checklist.html | ❌ Hardcoded | 100+ items | 83KB |
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
- Strollers already use JSON — we extend this pattern
- Products have different cost models:
- One-time purchase (strollers, bottles)
- Consumables (diapers, wipes, formula)
- Hybrid (breast pumps: device + consumable parts)
- 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
// ═══════════════════════════════════════════════════════════════════ // 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)
- Create JSON files from HTML (manual or scripted)
- Validate against TypeScript schema
- Update strollers.json to new format
Phase 2: Template Creation (2-3 days)
- Create shared Handlebars partials (nav, footer, cards)
- Create category-specific templates
- Test template output matches current HTML
Phase 3: Build System Setup (1-2 days)
- Set up Node.js build script
- Configure GitHub Actions CI/CD
- Test generated output
Phase 4: Cutover (1 day)
- Generate all HTML from data
- Visual diff testing
- 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
| Option | Pros | Cons | Verdict |
|---|---|---|---|
| Static Gen (Node.js) | Simple hosting, fast, SEO-perfect | No real-time data | ✅ Best fit |
| Next.js SSG | Great DX, React ecosystem | Overkill for static | Consider for v2 |
| Runtime JS | Dynamic features | Slower, client-dependent | ❌ |
Recommended Setup
```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
- Centralized JSON data - Single source of truth
- Type-safe schema - TypeScript prevents errors
- Static generation - Fast, SEO-optimized
- Future-ready - Easy to add APIs
Next Steps
- Start with diapers.json as pilot
- Create Handlebars template
- Validate build output
- 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