130,000+ customer accounts. Thousands of partner integrations. A Next.js 16 SPA, a Python Functions backend, RBAC enforced at every layer, and a 46-page handover doc that lets the client’s team take it from here. We didn’t deliver a system — we delivered operational independence.
A Fortune 500 global healthcare enterprise needed a centralized Enterprise Data Platform — internal command center for managing third-party provider integrations, customer accounts, and high-volume bulk operations across a global partner ecosystem. Tech Stack Playbook designed and delivered the entire platform on Azure: a Next.js 16 SPA, Python Azure Functions backend, Cosmos DB data store, and Microsoft Entra ID authentication.
Below: the live RBAC role-switcher that demonstrates how the same UI reconfigures across three permission tiers, the bulk operations engine, the architecture, and the handover discipline that makes a build-and-leave engagement actually transferable.
01 / BRIEFBuild It. Document It. Hand It Over.
Fortune 500 enterprises don’t hire agencies to build toys. They hire them to ship systems that manage critical operations at scale, integrate with existing enterprise infrastructure, pass internal security and compliance review, and — most importantly — can be operated by their own team after the engagement ends. That last constraint is where most consulting engagements quietly fail. Code gets written, the consultancy disappears, and the internal team inherits a system nobody can confidently change.
This engagement was scoped the opposite way. Tech Stack Playbook was hired to build, ship, and leave. Every architectural decision had to be defensible without us in the room. Every deployment workflow had to be runnable by an engineer who’d onboarded the day before. Every operational procedure needed a documented troubleshooting path. Same handover discipline that anchored our multi-account AWS modernization work for a multinational health research org — different cloud, same philosophy.
The platform manages relationships across 130,000+ customer accounts connecting thousands of manufacturers and technology partners with hundreds of thousands of healthcare practitioners. When a new partner gets onboarded, when customer relationships get reconciled at scale, when ops needs to know what’s happening across the partner ecosystem in real time — this is the system they use.
02 / RBACTap a Role. Watch the UI Reconfigure.
The platform serves three distinct user types, and the UI dynamically adjusts based on role claims in JWT tokens from Microsoft Entra ID. Switch roles below and watch what happens — destructive buttons disappear for read-only users, bulk operations gate behind admin permissions, and the read-only badge surfaces context everywhere. Role enforcement happens at the component level and at the API gateway, so the UI can’t be tampered into granting access the backend doesn’t independently confirm.
Switch to Read-Only and notice what the UI doesn’t do: it doesn’t disable buttons and pretend they exist. It removes them. The “New Partner” button is gone. Bulk Import is gone. The destructive trash icon on every row is gone. The read-only badge appears next to the page title, and the JWT scope changes to partners.read. That’s not aesthetic. It’s the implementation choice that prevents a curious user from devtools-removing a disabled attribute and accidentally calling a write API. The button isn’t there to find.
The two-layer enforcement model
UI conditional rendering is the first line of defense. It is not the only line. The Azure API Management gateway independently validates every request against the same role claims — so even if a determined user crafted an HTTP request directly, the gateway rejects writes the user’s role doesn’t authorize. Component-level enforcement keeps the UI honest. API-level enforcement keeps the data honest.
// Custom hook reads roles from MSAL session, surfaces typed predicates. export const useAuthz = () => { const { accounts } = useMsal(); const roles = (accounts[0]?.idTokenClaims?.roles ?? []) as string[]; return { canRead: true, // every authenticated user canWrite: roles.includes('EDP.Admin') || roles.includes('EDP.Staff'), canBulk: roles.includes('EDP.Admin') || roles.includes('EDP.Staff'), canConfig: roles.includes('EDP.Admin'), isReadOnly: !roles.some(r => ['EDP.Admin', 'EDP.Staff'].includes(r)), }; }; // Conditional rendering — the button doesn't exist for read-only users. const { canWrite, isReadOnly } = useAuthz(); return ( <PageHeader> <Title>Partner Integrations</Title> {isReadOnly && <ReadOnlyBadge/>} <Actions> <ExportButton /> // always visible {canWrite && <BulkImportButton />} {canWrite && <NewPartnerButton />} // removed for read-only </Actions> </PageHeader> );
03 / ARCHITECTUREThe Azure-Native Stack
The platform is a SPA with a service-layer architecture: presentation in Next.js 16 with static export, deployed to Azure Storage with Azure Front Door as the global CDN and WAF. Two Python Azure Functions services back it — Admin Backend for CRUD, TPP Backend for bulk ops. Cosmos DB as the NoSQL store, Azure API Management as the gateway, Microsoft Entra ID as the identity provider.
Why a SPA static export over server-rendered Next.js
Next.js 16 supports both. For an internal admin portal with no SEO surface — the platform isn’t a public site, it’s a credentialed tool — a static SPA hosted on Azure Storage behind Front Door is the simpler, cheaper, faster-to-deploy option. No always-on Node runtime to manage. CDN cache invalidation is one Azure CLI call. Sub-5-minute deploys without any of the SSR ops complexity. The decision tree is: if you don’t need SSR for SEO or per-request server logic, don’t pay for it.
Why two Function Apps instead of one
The admin CRUD API and the bulk ops API have very different runtime profiles. CRUD calls are short, latency-sensitive, and high-frequency. Bulk operations queue jobs that run for minutes against tens of thousands of records and need different memory configurations, timeout settings, and concurrency limits. Splitting them into separate Function Apps lets each scale independently and contains failures — a runaway bulk import doesn’t take down the entire admin UI.
04 / BULK OPSLive-Tracked Operations on Tens of Thousands of Records
Enterprise data management means operating on thousands of records at once with full visibility, validation, and rollback paths. The bulk operations engine is the platform’s most technically demanding component — and the one that delivers the most operational value to the team using it. Here’s a simulated bulk export running against the partner-customer relationship graph:
Three bulk operations ship in the platform: Export (configurable scope, format selection, full progress tracking), Import (Excel/CSV ingestion with pre-commit preview, duplicate detection, schema validation, and rollback), and Delete (two-step confirmation, affected-record preview, and selective scope via CSV upload). Every job lands in the job history with audit-grade metadata: operation type, status, timestamps, duration, record counts, and downloadable results — 90-day retention by default.
05 / HANDOVERWhat “Built for Handover” Actually Looks Like
The most consequential deliverable wasn’t code. It was the 46-page technical handover document. System architecture. Tech stack rationale for every choice. Every page, feature, and flow walked top to bottom. Authentication trees. API integration contracts. Every CI/CD pipeline diagrammed and annotated. Local dev setup that gets a new engineer productive in a single day. Documented troubleshooting paths for the five most common operational failure modes. A debugging toolkit covering DevTools procedures, verbose logging, and API testing scripts.
The handover document is the product.
The platform’s success isn’t measured by the day we shipped it. It’s measured by the day a new engineer at the enterprise opens the doc, sets up local dev, deploys a hotfix to QA, and goes home for dinner — without having ever spoken to anyone at TSP.
This is the discipline that separates a project from a partnership. The 12-step GitHub Actions deployment pipeline isn’t a black box — it’s documented step by step, with environment-scoped GitHub secrets, Slack notifications, deployment summaries, and a hotfix flow for when production needs a fast patch. Sub-5-minute commit-to-production. Code quality gates (ESLint, Prettier, TypeScript strict mode, Vitest) running on every PR before merge. Bundle-size budgets enforced — initial bundle stays under 300KB gzipped. Core Web Vitals tracked on every build (LCP < 2.5s, FID < 100ms, CLS < 0.1).
We delivered this engagement entirely on Azure — not AWS — because the engagement was about solving the client’s problem on their infrastructure, not ours. Multi-cloud fluency is what separates a cloud consulting firm from a platform-specific shop.
06 / OUTCOMESWhat Shipped
Production-grade portal with bulk import/export/delete across the full partner-customer relationship graph at enterprise scale.
Admin, Internal Staff, and Read-Only enforced at the component layer and again at the Azure API Management gateway.
Comprehensive technical documentation enabling the enterprise’s internal team to operate and extend the platform independently.
12-step GitHub Actions pipeline with quality gates, environment-scoped configuration, automatic CDN cache purging.
Stack
07 / TAKEAWAYMulti-Cloud Fluency Is the Product
Enterprises don’t pick their cloud based on what your consultancy prefers. They pick based on what their security team has already approved, what their existing footprint looks like, and what their procurement team has already negotiated. A consultancy that only ships AWS will lose the Azure engagements, and vice versa. That’s why we ship across both — and why this engagement matters as a portfolio piece. Full-stack engineering done well doesn’t care what the cloud-provider logo says. It cares whether the system holds up at 130K+ records and whether the team that inherits it can keep shipping.
Need an enterprise platform built — and built for handover?
We partner with Fortune 500 enterprises and high-growth scale-ups to design, ship, and transfer production-grade systems on AWS, Azure, and wherever you operate. Architecture-first, RBAC at every layer, IaC end-to-end, documented to the point your team owns it from day one.
Book a strategy call