[{"data":1,"prerenderedAt":621},["ShallowReactive",2],{"navigation":3,"/blog/case-studies/retail-ecommerce-rebuild":51,"/blog/case-studies/retail-ecommerce-rebuild-surround":616},[4],{"title":5,"path":6,"stem":7,"children":8,"page":34},"Blog","/blog","blog",[9,13,35,39,43,47],{"title":10,"path":11,"stem":12},"Using the official exchange rate in Venezuela's e-commerce","/blog/bcv-scraper","blog/bcv-scraper",{"title":14,"path":15,"stem":16,"children":17,"page":34},"Case Studies","/blog/case-studies","blog/case-studies",[18,22,26,30],{"title":19,"path":20,"stem":21},"Rebuilding an e-commerce platform on NestJS + Next.js + Medusa in three months","/blog/case-studies/retail-ecommerce-rebuild","blog/case-studies/retail-ecommerce-rebuild",{"title":23,"path":24,"stem":25},"Stabilizing an inherited e-commerce platform on NestJS + Strapi","/blog/case-studies/retail-ecommerce-recovery","blog/case-studies/retail-ecommerce-recovery",{"title":27,"path":28,"stem":29},"A nationwide mobile sales-quotation app, integrated with SAP","/blog/case-studies/retail-sap-quotation-app","blog/case-studies/retail-sap-quotation-app",{"title":31,"path":32,"stem":33},"Shipping a high-scale PWA from an inherited e-commerce project","/blog/case-studies/supermarket-pwa-recovery","blog/case-studies/supermarket-pwa-recovery",false,{"title":36,"path":37,"stem":38},"From Nuxt to Hugo and Back Again","/blog/from-nuxt-to-hugo","blog/from-nuxt-to-hugo",{"title":40,"path":41,"stem":42},"The Intern is downloading movies again","/blog/intern-movie-torrent","blog/intern-movie-torrent",{"title":44,"path":45,"stem":46},"Japanese input in openSUSE Tumbleweed's KDE","/blog/japanese-input","blog/japanese-input",{"title":48,"path":49,"stem":50},"Jhey Pi: an official landing site for an aspiring musician","/blog/jp-landing","blog/jp-landing",{"id":52,"title":19,"author":53,"body":59,"category":592,"client":593,"date":594,"description":595,"extension":596,"featured":597,"image":598,"meta":599,"minRead":600,"navigation":597,"ogImage":598,"outcome_headline":601,"path":20,"repoUrl":600,"role":602,"seo":603,"stack":604,"stem":21,"tags":608,"team_size":612,"type":613,"year":614,"__hash__":615},"blog/blog/case-studies/retail-ecommerce-rebuild.md",{"name":54,"username":55,"to":56,"avatar":57},"Iván Álvarez","ivanovertime","https://github.com/ivanovertime",{"src":58,"alt":54},"/avatar.jpg",{"type":60,"value":61,"toc":576},"minimark",[62,89,94,120,124,127,134,137,140,144,216,223,227,232,235,286,292,296,299,335,345,349,352,388,394,398,401,437,440,444,447,480,487,491,494,561,564,567],[63,64,65],"blockquote",{},[66,67,68,76,77,81,82,88],"p",{},[69,70],"u-icon",{"className":71,"name":75},[72,73,74],"inline","mr-1","align-[-2px]","i-lucide-paperclip"," ",[78,79,80],"strong",{},"Part of a pair."," This is Act 2 of two consecutive e-commerce engagements with the same retail organization. The recovery that made it possible — stabilizing the inherited NestJS + Strapi platform — is here: ",[83,84,85],"a",{"href":24},[86,87,23],"em",{},".",[90,91,93],"h2",{"id":92},"tldr","TL;DR",[95,96,97,109],"ul",{},[98,99,100,101,104,105,108],"li",{},"Follow-on to ",[83,102,103],{"href":24},"the recovery case study",": a from-scratch e-commerce platform on ",[78,106,107],{},"NestJS + Next.js + Medusa",", with a team of 4, delivered inside a three-month window.",[98,110,111,112,115,116,119],{},"We ",[78,113,114],{},"reused the SAP integration boundary"," from the recovery as a contract, kept Medusa to commerce (not as a CMS), and ",[78,117,118],{},"planned the cutover from day one"," instead of letting it slip into \"soft launch\" purgatory.",[90,121,123],{"id":122},"context","Context",[66,125,126],{},"The recovery had earned the platform back its release cadence and a stable SAP integration boundary. With the runtime settled, the question changed:",[63,128,129],{},[66,130,131],{},[86,132,133],{},"\"If you started over, what would you build?\"",[66,135,136],{},"The brief was a three-month window for a from-scratch rebuild.",[66,138,139],{},"That kind of brief — a real budget, a real deadline, a real reason to start fresh — is rare. The temptation is to spend it on novelty. The right answer was to spend it on the things that would still be true a year later.",[90,141,143],{"id":142},"constraints","Constraints",[145,146,147,160],"table",{},[148,149,150],"thead",{},[151,152,153,157],"tr",{},[154,155,156],"th",{},"Constraint",[154,158,159],{},"Reality",[161,162,163,172,180,192,200,208],"tbody",{},[151,164,165,169],{},[166,167,168],"td",{},"Team size",[166,170,171],{},"4 engineers, with me as tech lead",[151,173,174,177],{},[166,175,176],{},"Calendar",[166,178,179],{},"Three months, set by the business",[151,181,182,185],{},[166,183,184],{},"Inherited dependencies",[166,186,187,188,191],{},"None on the runtime — but ",[78,189,190],{},"SAP stays"," as catalog, pricing, and inventory source of truth",[151,193,194,197],{},[166,195,196],{},"Co-existence",[166,198,199],{},"Had to run alongside the recovered storefront until cutover",[151,201,202,205],{},[166,203,204],{},"Off-limits",[166,206,207],{},"Anything that broke continuity for customers",[151,209,210,213],{},[166,211,212],{},"Reusable assets",[166,214,215],{},"The SAP integration boundary built during the recovery",[66,217,218,219,222],{},"The non-negotiable: ",[78,220,221],{},"a real ship date inside the three-month window"," — no soft launch, no parallel-run-forever.",[90,224,226],{"id":225},"decisions","Decisions",[228,229,231],"h3",{"id":230},"decision-1-nestjs-nextjs-medusa-not-the-new-shiny-thing","Decision 1 — NestJS + Next.js + Medusa, not \"the new shiny thing\"",[66,233,234],{},"Three months and four engineers is not the budget for novelty risk. We picked tools the team could be productive in by week two.",[145,236,237,250],{},[148,238,239],{},[151,240,241,244,247],{},[154,242,243],{},"Option",[154,245,246],{},"Trade-off",[154,248,249],{},"Chose",[161,251,252,262],{},[151,253,254,257,260],{},[166,255,256],{},"Pick the most fashionable e-commerce framework of 2025",[166,258,259],{},"Novelty risk on a three-month deadline; longer ramp; smaller community when stuck",[166,261],{},[151,263,264,278,281],{},[166,265,266,269,270,273,274,277],{},[78,267,268],{},"NestJS"," for the backend (we already knew it from the recovery), ",[78,271,272],{},"Next.js"," for the storefront, ",[78,275,276],{},"Medusa"," as the commerce engine",[166,279,280],{},"Each piece is mature, well-documented, and replaceable; the team could ramp in days, not weeks",[166,282,283],{},[284,285],"decision-check",{},[66,287,288,289],{},"The novelty budget went into the SAP integration and the cutover plan, not into the framework choice. ",[78,290,291],{},"Boring on purpose.",[228,293,295],{"id":294},"decision-2-reuse-the-sap-integration-boundary-dont-reinvent-it","Decision 2 — Reuse the SAP integration boundary, don't reinvent it",[66,297,298],{},"The integration module from the recovery had a clear contract: typed interface, observable, retry-aware, one place to change. The rebuild adopted the same contract.",[145,300,301,311],{},[148,302,303],{},[151,304,305,307,309],{},[154,306,243],{},[154,308,246],{},[154,310,249],{},[161,312,313,323],{},[151,314,315,318,321],{},[166,316,317],{},"Build a new SAP integration alongside the old one",[166,319,320],{},"Two places to maintain; long parallel-run window; two sets of bugs",[166,322],{},[151,324,325,328,331],{},[166,326,327],{},"Lift the boundary contract from the recovery, port the implementation onto the new stack",[166,329,330],{},"Requires the recovery to actually be reusable; pays back the entire cost of having built it cleanly",[166,332,333],{},[284,334],{},[66,336,337,338,341,342,88],{},"This is the hidden return on the recovery work: ",[78,339,340],{},"the integration boundary became an asset, not a sunk cost."," The same pattern carries into the ",[83,343,344],{"href":28},"SAP quotation app",[228,346,348],{"id":347},"decision-3-medusa-as-a-commerce-engine-not-a-cms","Decision 3 — Medusa as a commerce engine, not a CMS",[66,350,351],{},"The previous build had let Strapi drift into a control plane for everything. We deliberately did not let Medusa repeat that pattern.",[145,353,354,364],{},[148,355,356],{},[151,357,358,360,362],{},[154,359,243],{},[154,361,246],{},[154,363,249],{},[161,365,366,376],{},[151,367,368,371,374],{},[166,369,370],{},"Stretch Medusa to cover editorial content, marketing pages, business logic",[166,372,373],{},"One tool for everything; everything becomes coupled to a commerce framework's release cycle",[166,375],{},[151,377,378,381,384],{},[166,379,380],{},"Keep Medusa to commerce: catalog, cart, checkout, orders. Editorial content stays in a content tool. SAP stays the system of record.",[166,382,383],{},"Each piece has one job; each piece is replaceable",[166,385,386],{},[284,387],{},[66,389,390,393],{},[78,391,392],{},"One typed boundary per external system, one tool per job."," The discipline is the same as the SAP boundary, applied internally.",[228,395,397],{"id":396},"decision-4-cutover-not-parallel-run-forever","Decision 4 — Cutover, not parallel-run-forever",[66,399,400],{},"Three months meant we couldn't afford to run both storefronts in parallel indefinitely. \"Soft launch\" is what platforms do when they don't trust their own ship date.",[145,402,403,413],{},[148,404,405],{},[151,406,407,409,411],{},[154,408,243],{},[154,410,246],{},[154,412,249],{},[161,414,415,425],{},[151,416,417,420,423],{},[166,418,419],{},"Soft launch behind a flag, indefinite parallel run",[166,421,422],{},"Delays the moment of truth; doubles ops cost; tempts the team to keep adding to both",[166,424],{},[151,426,427,430,433],{},[166,428,429],{},"Plan the cutover from day one — same SAP source of truth, same payment surfaces, scheduled switchover",[166,431,432],{},"Forces honest scope; turns the deadline into a real ship date",[166,434,435],{},[284,436],{},[66,438,439],{},"Same SAP. Same payments. A real cutover date on the calendar from week one. The deadline became a feature.",[90,441,443],{"id":442},"outcome","Outcome",[66,445,446],{},"By the end of the three months:",[95,448,449,459,466,472,475],{},[98,450,451,452,454,455,458],{},"A from-scratch ",[78,453,107],{}," platform was ",[78,456,457],{},"delivered inside the window"," the business had set.",[98,460,461,462,465],{},"The ",[78,463,464],{},"SAP integration boundary"," built during the recovery was reused as the contract and validated by a second consumer.",[98,467,468,471],{},[78,469,470],{},"Customer continuity was preserved"," through cutover — same source of truth, scheduled switchover, no extended dual-run.",[98,473,474],{},"The product team continued to plan a roadmap on a platform they could now actually ship against.",[98,476,477,478,88],{},"The integration-module pattern proved reusable across other platforms in the same engagement — most directly the ",[83,479,344],{"href":28},[66,481,482,483,486],{},"The deeper lesson is the one that made the engagement work at all: ",[78,484,485],{},"diagnose, contain, then (only then) consider rebuilding"," — never all three on the same day. The recovery earned the right to do this.",[90,488,490],{"id":489},"what-id-do-on-gcp-today","What I'd do on GCP today",[66,492,493],{},"Same brief in 2026, same team, same SAP, same three months. The architectural instincts wouldn't change — but the platform would do more of the work.",[95,495,496,506,519,529,543,552],{},[98,497,498,501,502,505],{},[78,499,500],{},"Backend runtime:"," containerize NestJS for ",[78,503,504],{},"Cloud Run",". Same artifact, no host-management work.",[98,507,508,511,512,514,515,518],{},[78,509,510],{},"Storefront:"," Next.js on ",[78,513,504],{}," for SSR, with static assets on ",[78,516,517],{},"Firebase Hosting"," in front. Lighthouse perf as a CI gate from day one.",[98,520,521,524,525,528],{},[78,522,523],{},"SAP integration:"," keep the single-module pattern. Put ",[78,526,527],{},"Pub/Sub"," between SAP events and the NestJS consumer so the storefront degrades gracefully when SAP is slow, and the integration module becomes a Pub/Sub consumer with retries and a dead-letter topic.",[98,530,531,534,535,538,539,542],{},[78,532,533],{},"Catalog & pricing reads:"," project SAP data into ",[78,536,537],{},"Firestore"," for hot reads and into ",[78,540,541],{},"BigQuery"," for analytics, with SAP unchanged as the system of record.",[98,544,545,76,548,551],{},[78,546,547],{},"Observability:",[78,549,550],{},"Cloud Logging + Cloud Trace + Error Reporting"," from week one. A rebuild without observability is a recovery in waiting.",[98,553,554,76,557,560],{},[78,555,556],{},"Delivery:",[78,558,559],{},"GitHub Actions → Cloud Run + Firebase",", preview environment per PR, blue-green for the storefront. Cutover becomes a DNS flip rather than a maintenance window.",[66,562,563],{},"The pattern is the same as the recovery. The leverage is higher, and the cutover gets cheaper.",[565,566],"hr",{},[66,568,569],{},[86,570,571,572,88],{},"If you're scoping a \"build it properly this time\" e-commerce engagement and the calendar is honest about the constraint, that's the kind of work I do best. Happy to talk through it — ",[83,573,575],{"href":574},"/contact","get in touch",{"title":577,"searchDepth":578,"depth":578,"links":579},"",2,[580,581,582,583,590,591],{"id":92,"depth":578,"text":93},{"id":122,"depth":578,"text":123},{"id":142,"depth":578,"text":143},{"id":225,"depth":578,"text":226,"children":584},[585,587,588,589],{"id":230,"depth":586,"text":231},3,{"id":294,"depth":586,"text":295},{"id":347,"depth":586,"text":348},{"id":396,"depth":586,"text":397},{"id":442,"depth":578,"text":443},{"id":489,"depth":578,"text":490},"case-study"," Retail Client (LatAm)","2026-05-14T00:00:00.000Z","After the recovery, the next engagement was a from-scratch e-commerce build on NestJS + Next.js + Medusa, delivered with a team of 4 inside a three-month window. SAP stayed as the source of truth, the integration boundary from the recovery was reused as a contract, and the cutover was planned from day one.\n","md",true,"/blog/case-studies/retail-ecommerce-core/featuredImage.jpg",{},null,"Greenfield e-commerce platform delivered inside the three-month window, reusing the SAP integration boundary built during the recovery","Tech Lead",{"title":19,"description":595},[268,272,276,605,606,607],"Docker","SAP","CI/CD",[609,610,611],"e-commerce","nestjs","nuxt",4,"greenfield",2025,"cYVovYZz_PBZ4oJCWUPhW1mc7y055ZE7pNcR3pZ9yxE",[617,619],{"title":10,"path":11,"stem":12,"description":618,"children":-1},"Venezuela has unique challenges for e-commerce. Local laws and tax regulations require invoices in bolívares while prices are quoted in USD. A simple Python and FastAPI scraper helps developers fetch the Banco Central de Venezuela exchange rate—here's when it makes sense and how to build it.",{"title":23,"path":24,"stem":25,"description":620,"children":-1},"A recovery-shaped engagement on an inherited NestJS + Strapi storefront: stabilize the runtime, contain the SAP integration behind a single boundary, containerize the deploy path, and keep Strapi to editorial content. This is the recovery half of a paired engagement; the rebuild that followed is its own story.\n",1779739959340]