{"id":528,"date":"2026-04-14T09:37:31","date_gmt":"2026-04-14T09:37:31","guid":{"rendered":"https:\/\/www.devopsschool.com\/tutorials\/azure-fluid-relay-tutorial-architecture-pricing-use-cases-and-hands-on-guide-for-web\/"},"modified":"2026-04-14T09:37:31","modified_gmt":"2026-04-14T09:37:31","slug":"azure-fluid-relay-tutorial-architecture-pricing-use-cases-and-hands-on-guide-for-web","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/tutorials\/azure-fluid-relay-tutorial-architecture-pricing-use-cases-and-hands-on-guide-for-web\/","title":{"rendered":"Azure Fluid Relay Tutorial: Architecture, Pricing, Use Cases, and Hands-On Guide for Web"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Category<\/h2>\n\n\n\n<p>Web<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Introduction<\/h2>\n\n\n\n<p>Azure Fluid Relay is an Azure managed service that enables <strong>real-time, multi-user collaboration<\/strong> in web applications built with the <strong>Fluid Framework<\/strong> (Microsoft\u2019s open-source collaboration framework). It provides the hosted collaboration backend so your app can synchronize shared state\u2014like text, cursors, votes, timers, task boards, or whiteboards\u2014across many clients with low latency.<\/p>\n\n\n\n<p>In simple terms: <strong>Azure Fluid Relay lets multiple users open the same web page and see each other\u2019s changes instantly<\/strong>, without you having to build and operate your own real-time messaging, ordering, and state persistence infrastructure.<\/p>\n\n\n\n<p>Technically, Azure Fluid Relay provides the managed endpoints used by Fluid clients to create and join collaborative \u201ccontainers\u201d (sessions\/documents). It handles the server-side coordination required for consistent shared state, including processing real-time operations and storing document summaries\/snapshots (implementation details are abstracted, but the service provides the Fluid \u201ccollaboration service\u201d that Fluid clients talk to).<\/p>\n\n\n\n<p><strong>What problem it solves:<\/strong> building reliable, secure, scalable collaboration backends is hard. You need to manage websockets, message ordering, reconnection, persistence, concurrency, and security tokens\u2014all while keeping latency low and operational overhead reasonable. Azure Fluid Relay is designed to offload that backend complexity so you can focus on the collaborative UI and business logic.<\/p>\n\n\n\n<blockquote>\n<p>Note on naming: <strong>Azure Fluid Relay<\/strong> is a Web collaboration service. It is <strong>not<\/strong> the same as <strong>Azure Relay<\/strong> (which is a networking\/service-bus-related relay for exposing on-premises services). The names are similar, but the products solve different problems.<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">2. What is Azure Fluid Relay?<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Official purpose<\/h3>\n\n\n\n<p>Azure Fluid Relay is the Azure-hosted relay service for applications using the <strong>Fluid Framework<\/strong>. It provides a managed collaboration service so Fluid-enabled clients can connect to a shared document\/container and exchange operations in real time.<\/p>\n\n\n\n<p>Official documentation entry point (verify latest):<br\/>\nhttps:\/\/learn.microsoft.com\/azure\/azure-fluid-relay\/<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Core capabilities<\/h3>\n\n\n\n<p>At a high level, Azure Fluid Relay enables you to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Host Fluid containers<\/strong> in a managed Azure service.<\/li>\n<li><strong>Create and join collaborative sessions<\/strong> from web apps (and other client types supported by Fluid).<\/li>\n<li><strong>Synchronize distributed data structures<\/strong> (DDS) like shared maps, shared strings, or custom collaborative objects.<\/li>\n<li><strong>Authenticate clients<\/strong> via supported token approaches (commonly via an application-managed token service; options and best practices vary\u2014verify in official docs for the latest guidance).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Major components (conceptual)<\/h3>\n\n\n\n<p>Although Azure abstracts the internal implementation, you typically interact with these components:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Azure Fluid Relay resource (Azure resource provider)<\/strong>: the Azure resource you provision in a region.<\/li>\n<li><strong>Tenant<\/strong>: a logical grouping under the relay resource that your apps use to connect (tenants have identifiers and credentials).<\/li>\n<li><strong>Endpoint<\/strong>: the service URL your clients connect to.<\/li>\n<li><strong>Client SDK \/ libraries<\/strong>: typically the Fluid Framework packages plus an Azure Fluid Relay client (for example, <code>@fluidframework\/azure-client<\/code>).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Service type<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Managed PaaS<\/strong> collaboration backend for <strong>real-time Web<\/strong> application experiences (and related client platforms supported by Fluid).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Scope: regional vs global, subscription vs tenant<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Provisioned as an Azure resource in a region<\/strong>, within a subscription and resource group.<\/li>\n<li>Your application typically connects to a <strong>regional endpoint<\/strong>.<\/li>\n<li>Within the resource, you work with <strong>tenants<\/strong> for organizational and security boundaries.<\/li>\n<li>For the latest region availability and data residency details, <strong>verify in official docs<\/strong> (region availability can change).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">How it fits into the Azure ecosystem<\/h3>\n\n\n\n<p>Azure Fluid Relay is usually part of a Web application architecture that includes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Frontend<\/strong>: SPA (React\/Angular\/Vue), Next.js, ASP.NET, etc.<\/li>\n<li><strong>Token service<\/strong> (recommended): a small backend that issues short-lived tokens for clients (often hosted on <strong>Azure Functions<\/strong>, <strong>App Service<\/strong>, <strong>Container Apps<\/strong>, or <strong>AKS<\/strong>).<\/li>\n<li><strong>Identity provider<\/strong>: commonly <strong>Microsoft Entra ID (Azure AD)<\/strong> for user sign-in, plus an app-to-service mechanism for relay access (exact model depends on current supported auth patterns\u2014verify).<\/li>\n<li><strong>Observability<\/strong>: <strong>Azure Monitor<\/strong> and <strong>Application Insights<\/strong> for your app; any service-side diagnostics\/metrics available for Azure Fluid Relay should be enabled via Azure\u2019s standard monitoring configuration (verify what signals the service exposes).<\/li>\n<li><strong>Secrets<\/strong>: <strong>Azure Key Vault<\/strong> for tenant keys\/secrets used by token services.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">3. Why use Azure Fluid Relay?<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Business reasons<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Faster time to market<\/strong> for collaborative features (co-editing, shared dashboards, multiplayer UX).<\/li>\n<li><strong>Reduced operational burden<\/strong>: avoid building and operating a custom real-time collaboration backend.<\/li>\n<li><strong>Better user engagement<\/strong>: real-time experiences can improve adoption and retention in productivity and workflow apps.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Technical reasons<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Fluid Framework integration<\/strong>: purpose-built to work with Fluid containers and distributed data structures.<\/li>\n<li><strong>Consistency and ordering<\/strong>: collaboration requires reliable operation ordering and state convergence.<\/li>\n<li><strong>Low-latency sync<\/strong>: designed for interactive experiences.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Operational reasons<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Managed service<\/strong>: fewer moving parts than self-hosting Fluid server components.<\/li>\n<li><strong>Scales with usage<\/strong>: you typically don\u2019t manage server fleets directly (exact scaling characteristics depend on the service and pricing tier\u2014verify).<\/li>\n<li><strong>Azure-native management<\/strong>: resource groups, RBAC, tags, policies, and standard operational patterns.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Security\/compliance reasons<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Supports <strong>Azure resource governance<\/strong> (RBAC, resource locks, policies).<\/li>\n<li>Enables <strong>centralized secret management<\/strong> (Key Vault) and <strong>least-privilege<\/strong> token issuance patterns through your own token service.<\/li>\n<li>Region selection supports data residency choices (verify specifics per region and compliance scope in official docs).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Scalability\/performance reasons<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Better than \u201crolling your own\u201d websockets + database approach for collaboration correctness.<\/li>\n<li>Designed for <strong>many concurrent clients<\/strong> collaborating on shared state.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">When teams should choose it<\/h3>\n\n\n\n<p>Choose Azure Fluid Relay when:\n&#8211; You need <strong>true multi-user real-time collaboration<\/strong> (co-authoring) in a web app.\n&#8211; You want to build on the <strong>Fluid Framework<\/strong> ecosystem and data structures.\n&#8211; You prefer a managed service rather than operating custom collaboration servers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">When teams should not choose it<\/h3>\n\n\n\n<p>Avoid or reconsider Azure Fluid Relay when:\n&#8211; You only need <strong>one-way broadcasts<\/strong> or simple pub\/sub; <strong>Azure Web PubSub<\/strong> or <strong>Azure SignalR Service<\/strong> may be simpler.\n&#8211; Your collaboration model is \u201cevent stream\u201d rather than shared state convergence (again, Web PubSub\/SignalR or Event Hubs patterns may fit better).\n&#8211; You require <strong>full control<\/strong> over collaboration server internals, custom persistence, or on-prem-only operation (self-hosting could be required).\n&#8211; You have strict constraints where the service isn\u2019t available in required regions or doesn\u2019t meet compliance needs (verify).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Where is Azure Fluid Relay used?<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Industries<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SaaS productivity (docs, notes, project management)<\/li>\n<li>Education (shared whiteboards, classroom collaboration)<\/li>\n<li>Finance (shared dashboards, incident war rooms)<\/li>\n<li>Healthcare (collaborative care coordination\u2014subject to compliance requirements)<\/li>\n<li>Gaming and interactive media (lightweight shared state, lobbies, collaborative experiences)<\/li>\n<li>Customer support (shared troubleshooting sessions)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Team types<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Frontend engineers building collaborative UIs<\/li>\n<li>Full-stack product teams<\/li>\n<li>Platform teams providing \u201ccollaboration as a platform capability\u201d<\/li>\n<li>Architects building interactive, multi-user web systems<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Workloads<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Real-time collaborative editing and annotation<\/li>\n<li>Shared task boards, kanban, and planning tools<\/li>\n<li>Multiplayer-style shared state experiences in the browser<\/li>\n<li>Co-browsing-like experiences (shared pointers, highlights)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Architectures<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SPA + token service + Azure Fluid Relay<\/li>\n<li>Multi-tenant SaaS where each customer maps to a tenant\/app-level partition<\/li>\n<li>Hybrid architectures where existing data is stored in databases, while \u201clive session state\u201d is synchronized via Fluid containers<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Production vs dev\/test usage<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Dev\/test<\/strong>: quick prototypes using local token services and minimal auth, validating collaboration flows.<\/li>\n<li><strong>Production<\/strong>: hardened token issuance, Entra ID integration, Key Vault-managed secrets, observability, capacity planning, and governance.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">5. Top Use Cases and Scenarios<\/h2>\n\n\n\n<p>Below are realistic scenarios where Azure Fluid Relay is a strong fit. Each includes the problem, why it fits, and a short example.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1) Collaborative text editor (co-authoring)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> Multiple users need to edit the same document simultaneously with near-instant updates.<\/li>\n<li><strong>Why Azure Fluid Relay fits:<\/strong> Fluid containers and shared text structures enable consistent real-time co-authoring behavior.<\/li>\n<li><strong>Example:<\/strong> A knowledge base editor where multiple agents update a troubleshooting article during an incident.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2) Shared whiteboard \/ drawing canvas<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> Users need a shared canvas where strokes and shapes appear live for everyone.<\/li>\n<li><strong>Why it fits:<\/strong> A shared data structure can represent drawing operations; relay synchronizes operations across clients.<\/li>\n<li><strong>Example:<\/strong> Remote design reviews where participants sketch proposed UI changes together.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3) Real-time task board (Kanban)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> Task cards move across columns in real time for distributed teams.<\/li>\n<li><strong>Why it fits:<\/strong> Shared maps\/lists represent columns and cards; updates propagate instantly.<\/li>\n<li><strong>Example:<\/strong> Sprint planning board where participants drag cards simultaneously.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4) Live meeting notes and action items<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> Meeting notes need co-editing and live action-item tracking.<\/li>\n<li><strong>Why it fits:<\/strong> Shared text + shared maps for action items; users see changes immediately.<\/li>\n<li><strong>Example:<\/strong> A meeting app that includes a shared agenda and decisions log.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">5) Shared incident \u201cwar room\u201d dashboard<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> During outages, teams need to coordinate live\u2014status, owner assignments, mitigations.<\/li>\n<li><strong>Why it fits:<\/strong> Low-latency shared state reduces confusion; everyone stays in sync.<\/li>\n<li><strong>Example:<\/strong> Incident commander updates current hypothesis, owners, and ETAs in a shared panel.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">6) Multiplayer quiz \/ live poll<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> Many participants answer questions; results update live.<\/li>\n<li><strong>Why it fits:<\/strong> Shared counters\/maps can aggregate answers; clients render live statistics.<\/li>\n<li><strong>Example:<\/strong> A webinar tool showing real-time poll bars as attendees vote.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">7) Collaborative code review annotations (lightweight)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> Reviewers want to comment together on code snippets in real time.<\/li>\n<li><strong>Why it fits:<\/strong> Shared data structures store annotations and positions; relay syncs them live.<\/li>\n<li><strong>Example:<\/strong> Pair-programming review session that includes shared highlights and comments.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">8) Shared cursor and presence in a document<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> Users want to see who is present and where they are working.<\/li>\n<li><strong>Why it fits:<\/strong> Presence can be modeled as shared ephemeral state; clients broadcast updates through the container.<\/li>\n<li><strong>Example:<\/strong> A document editor showing avatars and cursors for each collaborator.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">9) Interactive product configurator (co-shopping \/ co-design)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> Two users design a product together (colors, components) while chatting on a call.<\/li>\n<li><strong>Why it fits:<\/strong> Shared configuration state stays consistent across clients.<\/li>\n<li><strong>Example:<\/strong> A furniture configurator where customer and sales rep adjust options together.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">10) Training labs with shared progress<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> Instructors want to see learner progress live and help in real time.<\/li>\n<li><strong>Why it fits:<\/strong> Shared states track checkpoints; instructors can \u201cjoin\u201d and observe state changes.<\/li>\n<li><strong>Example:<\/strong> A cloud training platform where a mentor sees a learner\u2019s lab steps in real time.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">11) Collaborative data labeling session<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> Multiple labelers annotate items; conflicts and duplicate work must be minimized.<\/li>\n<li><strong>Why it fits:<\/strong> Shared work queue and lock state via shared maps\/lists.<\/li>\n<li><strong>Example:<\/strong> Labeling medical images where each item is assigned in real time.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">12) Shared timer and state for online workshops<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> Everyone needs synchronized timers and prompts.<\/li>\n<li><strong>Why it fits:<\/strong> Shared timer state ensures consistency.<\/li>\n<li><strong>Example:<\/strong> A facilitation tool for workshops with breakout timers.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">6. Core Features<\/h2>\n\n\n\n<blockquote>\n<p>The exact feature set can evolve. For the most current list (including limits and auth options), verify in official docs: https:\/\/learn.microsoft.com\/azure\/azure-fluid-relay\/<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">1) Managed Fluid collaboration endpoint<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>What it does:<\/strong> Provides hosted endpoints that Fluid clients connect to for collaboration.<\/li>\n<li><strong>Why it matters:<\/strong> Eliminates the need to run\/scale your own collaboration servers.<\/li>\n<li><strong>Practical benefit:<\/strong> Faster setup; fewer operational responsibilities.<\/li>\n<li><strong>Caveats:<\/strong> Region availability and service quotas may affect your design\u2014verify.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2) Tenants for logical separation<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>What it does:<\/strong> Enables grouping and scoping of collaboration usage.<\/li>\n<li><strong>Why it matters:<\/strong> Helps isolate environments (dev\/test\/prod) or customer partitions.<\/li>\n<li><strong>Practical benefit:<\/strong> Cleaner governance, credential separation, and lifecycle management.<\/li>\n<li><strong>Caveats:<\/strong> Tenant limits and management workflows vary\u2014verify current tenant model.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3) Compatibility with Fluid Framework DDS (shared data structures)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>What it does:<\/strong> Works with Fluid Framework distributed data structures such as shared maps and shared strings.<\/li>\n<li><strong>Why it matters:<\/strong> DDS lets you model collaborative state without designing low-level sync logic.<\/li>\n<li><strong>Practical benefit:<\/strong> Build collaborative features (notes, boards, counters) quickly.<\/li>\n<li><strong>Caveats:<\/strong> You still must design data modeling carefully to avoid excessive churn\/ops.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4) Real-time operation relay for connected clients<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>What it does:<\/strong> Relays operations between clients in near real time.<\/li>\n<li><strong>Why it matters:<\/strong> Collaboration requires fast propagation of changes.<\/li>\n<li><strong>Practical benefit:<\/strong> Smooth user experience with minimal custom networking.<\/li>\n<li><strong>Caveats:<\/strong> Performance depends on client behavior, payload sizes, and network conditions.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">5) Document\/container lifecycle: create and join<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>What it does:<\/strong> Allows apps to create new collaborative containers and let users join existing ones.<\/li>\n<li><strong>Why it matters:<\/strong> Enables shareable URLs and \u201cjoin this session\u201d functionality.<\/li>\n<li><strong>Practical benefit:<\/strong> Straightforward \u201cCreate session\u201d and \u201cJoin session\u201d flows.<\/li>\n<li><strong>Caveats:<\/strong> You must secure the join flow and prevent unauthorized access via your token service.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">6) Auth via token issuance pattern (recommended for production)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>What it does:<\/strong> Your app issues short-lived tokens to clients so they can connect to the relay.<\/li>\n<li><strong>Why it matters:<\/strong> Prevents long-lived secrets from being embedded in browsers.<\/li>\n<li><strong>Practical benefit:<\/strong> Centralized access control (users, roles, scopes) and auditability.<\/li>\n<li><strong>Caveats:<\/strong> The exact token format and supported providers must match current docs; implement carefully and rotate keys.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">7) Azure resource governance (RBAC, tags, locks, policy)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>What it does:<\/strong> Uses Azure standard governance controls for the relay resource.<\/li>\n<li><strong>Why it matters:<\/strong> Operational control, least privilege, and consistent resource management.<\/li>\n<li><strong>Practical benefit:<\/strong> Integrates with enterprise Azure controls.<\/li>\n<li><strong>Caveats:<\/strong> Azure RBAC governs management-plane actions; data-plane access is typically governed by app-issued tokens.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">8) Integration pattern with Azure hosting for token service<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>What it does:<\/strong> Commonly used with Azure Functions\/App Service\/Container Apps to host a token service.<\/li>\n<li><strong>Why it matters:<\/strong> Keeps secrets server-side and supports enterprise auth patterns.<\/li>\n<li><strong>Practical benefit:<\/strong> Cleaner security architecture.<\/li>\n<li><strong>Caveats:<\/strong> You own and must secure\/monitor the token service.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">7. Architecture and How It Works<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">High-level architecture<\/h3>\n\n\n\n<p>A typical Azure Fluid Relay solution has three parts:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Client app (web)<\/strong>: Uses Fluid Framework + Azure client library to create\/join containers and read\/write shared state.<\/li>\n<li><strong>Token service (backend)<\/strong>: Authenticates the user (optional but common) and issues a short-lived token granting access to a specific tenant\/container with specific permissions.<\/li>\n<li><strong>Azure Fluid Relay<\/strong>: Manages the collaboration session\u2014relaying operations and storing the collaborative document state (the service abstracts internal storage and ordering).<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Request \/ data \/ control flow (conceptual)<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>User signs in to your web app (often Entra ID, but could be any IdP).<\/li>\n<li>Web app requests a Fluid access token from your backend token service.<\/li>\n<li>Token service validates the user and issues a token with appropriate claims\/scopes.<\/li>\n<li>Web client connects to the Azure Fluid Relay endpoint using that token.<\/li>\n<li>When a user changes shared state, the client sends operations to the relay.<\/li>\n<li>Relay distributes operations to other connected clients, which apply them locally.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Integrations with related Azure services<\/h3>\n\n\n\n<p>Common integrations include:\n&#8211; <strong>Azure Functions \/ App Service \/ Container Apps<\/strong>: host the token service.\n&#8211; <strong>Azure Key Vault<\/strong>: store tenant keys or signing secrets.\n&#8211; <strong>Azure Monitor \/ Application Insights<\/strong>: observe token service and app performance; enable relay diagnostics if available.\n&#8211; <strong>Azure Front Door<\/strong>: global entry point for your web app and token service.\n&#8211; <strong>Microsoft Entra ID<\/strong>: user identity and access control for your app.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Dependency services<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Your app hosting (frontend)<\/li>\n<li>Token service hosting (backend)<\/li>\n<li>Identity provider for user login (optional but strongly recommended)<\/li>\n<li>Secret store (Key Vault) for production deployments<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Security\/authentication model (practical view)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Management plane:<\/strong> Azure RBAC controls who can create\/manage the Azure Fluid Relay resource and tenants.<\/li>\n<li><strong>Data plane:<\/strong> Clients typically authenticate using short-lived tokens issued by your app\/token service. Do <strong>not<\/strong> embed long-lived tenant secrets in a browser app for production.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Networking model<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Clients connect over the public internet to the relay endpoint (typical).<\/li>\n<li>Your token service can be public (with strong auth) or private behind an API gateway depending on your architecture.<\/li>\n<li>Private networking options (Private Link \/ VNet integration) are service-specific; <strong>verify in official docs<\/strong> whether Azure Fluid Relay supports them and under what constraints.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Monitoring\/logging\/governance considerations<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Monitor your <strong>token service<\/strong> like any other API: latency, error rates, auth failures.<\/li>\n<li>Track usage patterns (active sessions, token issuance rates) and implement throttling\/backoff.<\/li>\n<li>If Azure Fluid Relay exposes Azure Monitor metrics\/diagnostics, enable them and route logs to Log Analytics (verify availability and schema).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Simple architecture diagram (Mermaid)<\/h3>\n\n\n\n<pre><code class=\"language-mermaid\">flowchart LR\n  U[Users in browser] --&gt; W[Web App (SPA)]\n  W --&gt;|Request token| TS[Token Service (Azure Functions\/App Service)]\n  TS --&gt;|Issue short-lived Fluid token| W\n  W --&gt;|Connect + ops| AFR[Azure Fluid Relay]\n  AFR --&gt;|Real-time updates| W\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Production-style architecture diagram (Mermaid)<\/h3>\n\n\n\n<pre><code class=\"language-mermaid\">flowchart TB\n  subgraph Internet\n    B1[Browser 1]\n    B2[Browser 2]\n  end\n\n  subgraph Azure\n    FD[Azure Front Door \/ CDN]\n    SPA[Static Web App \/ App Service hosting SPA]\n    API[Token API (Functions\/App Service\/Container Apps)]\n    KV[Azure Key Vault]\n    AI[Application Insights]\n    MON[Azure Monitor \/ Log Analytics]\n    AFR[Azure Fluid Relay (regional)]\n  end\n\n  B1 --&gt; FD\n  B2 --&gt; FD\n\n  FD --&gt; SPA\n  FD --&gt; API\n\n  API --&gt; KV\n  SPA --&gt;|Auth flow| API\n  API --&gt;|Issue short-lived token| SPA\n\n  B1 --&gt;|Connect to relay endpoint| AFR\n  B2 --&gt;|Connect to relay endpoint| AFR\n\n  SPA --&gt; AI\n  API --&gt; AI\n  AI --&gt; MON\n  AFR -. diagnostics\/metrics if supported .-&gt; MON\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">8. Prerequisites<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Azure account and subscription<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>An active <strong>Azure subscription<\/strong>.<\/li>\n<li>Ability to create resources in a resource group in your target region.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Permissions \/ IAM roles<\/h3>\n\n\n\n<p>You\u2019ll typically need:\n&#8211; <strong>Contributor<\/strong> (or Owner) on the subscription\/resource group to create Azure Fluid Relay resources.\n&#8211; If using Key Vault later: permissions to create\/read secrets (Key Vault Administrator or a more limited RBAC role).\n&#8211; For production governance: ability to configure diagnostic settings and monitoring.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Billing requirements<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A billing-enabled subscription (Pay-As-You-Go, EA, MCA, etc.).<\/li>\n<li>Some orgs restrict preview services or regions; confirm with your Azure admin.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Tools for the lab<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Node.js LTS<\/strong> (recommend Node 18+ or 20+).<\/li>\n<li><strong>npm<\/strong> (or pnpm\/yarn).<\/li>\n<li>A code editor (VS Code recommended).<\/li>\n<li>Optional: <strong>Azure CLI<\/strong> for resource management (portal is fine too).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Region availability<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Azure Fluid Relay is <strong>regional<\/strong>. Choose a region close to your users.<\/li>\n<li>Confirm the service is available in your chosen region:<br\/>\n  https:\/\/learn.microsoft.com\/azure\/azure-fluid-relay\/ (check region availability in docs)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Quotas\/limits<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Quotas exist for tenants, connections, operations, etc. <strong>Verify in official docs<\/strong> because these values can change.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Prerequisite services (recommended for production)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A small backend \u201ctoken service\u201d (can be Azure Functions\/App Service).<\/li>\n<li>Azure Key Vault to store secrets.<\/li>\n<li>Entra ID app registration for user auth (recommended).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">9. Pricing \/ Cost<\/h2>\n\n\n\n<blockquote>\n<p>Do not rely on assumptions for exact costs. Always validate pricing in the official pricing page and calculator for your region and expected usage.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Official pricing resources<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Azure Fluid Relay pricing page (verify):<br\/>\n  https:\/\/azure.microsoft.com\/pricing\/details\/fluid-relay\/<\/li>\n<li>Azure Pricing Calculator:<br\/>\n  https:\/\/azure.microsoft.com\/pricing\/calculator\/<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Pricing model (how you should think about it)<\/h3>\n\n\n\n<p>Azure Fluid Relay is typically <strong>usage-based<\/strong>. Pricing dimensions commonly include (exact meters can vary\u2014verify on the pricing page):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Connection usage<\/strong> (for example, active client connections and\/or connection time)<\/li>\n<li><strong>Operations\/messages<\/strong> (the volume of collaboration operations relayed)<\/li>\n<li><strong>Storage<\/strong> (for persisted document summaries\/snapshots)<\/li>\n<li><strong>Data transfer (egress)<\/strong> (standard Azure bandwidth charges may apply depending on traffic patterns and region)<\/li>\n<\/ul>\n\n\n\n<p>If the service offers tiers (for example, free\/dev vs standard), confirm:\n&#8211; included quotas,\n&#8211; overage rates,\n&#8211; per-region pricing differences.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cost drivers<\/h3>\n\n\n\n<p>The main cost drivers in real-world deployments are:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\n<p><strong>Number of concurrent collaborators<\/strong>\n   &#8211; More users connected simultaneously increases connection-related consumption.<\/p>\n<\/li>\n<li>\n<p><strong>Operation rate<\/strong>\n   &#8211; A shared cursor sending updates every few milliseconds can generate huge operation volume.\n   &#8211; Frequent small edits can be more expensive than occasional large changes depending on how meters are defined.<\/p>\n<\/li>\n<li>\n<p><strong>Container\/document count and summary frequency<\/strong>\n   &#8211; More documents and more frequent summarization can increase storage and operations.<\/p>\n<\/li>\n<li>\n<p><strong>Geography<\/strong>\n   &#8211; Cross-region users may increase latency and egress costs.<\/p>\n<\/li>\n<li>\n<p><strong>Token service hosting<\/strong>\n   &#8211; App Service\/Functions costs, plus outbound bandwidth and monitoring.<\/p>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Hidden or indirect costs<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Token service compute<\/strong>: CPU\/time to validate users and mint tokens.<\/li>\n<li><strong>Observability<\/strong>: Log Analytics ingestion and retention can become a material cost.<\/li>\n<li><strong>Key Vault<\/strong>: per-operation and secret storage costs.<\/li>\n<li><strong>Bandwidth<\/strong>: especially if many clients are outside the relay\u2019s region.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Network\/data transfer implications<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Most collaboration traffic is <strong>client \u2194 relay<\/strong>. If clients are globally distributed, choose a region strategically.<\/li>\n<li>Egress costs depend on where clients are and how Azure bills outbound traffic.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">How to optimize cost (practical tactics)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Throttle \u201cpresence\u201d updates<\/strong> (cursor position, typing indicators). Send at a reasonable interval (for example, 5\u201310 updates\/sec rather than 60\/sec), and only when changes occur.<\/li>\n<li><strong>Minimize payload size<\/strong> in shared objects and operations.<\/li>\n<li><strong>Choose the right data model<\/strong>: don\u2019t store large blobs directly in Fluid shared structures; store references and keep large content in Blob Storage if needed.<\/li>\n<li><strong>Separate dev\/test resources<\/strong> from production; delete test tenants\/documents regularly.<\/li>\n<li><strong>Implement lifecycle cleanup<\/strong>: retire unused documents; avoid infinite growth.<\/li>\n<li><strong>Right-size logging<\/strong>: keep debug logs out of production and set reasonable retention.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Example low-cost starter estimate (conceptual)<\/h3>\n\n\n\n<p>A small prototype with:\n&#8211; a few collaborators at a time (2\u201310),\n&#8211; light shared state (a counter or small shared map),\n&#8211; short sessions,\nmay cost very little, but <strong>exact cost depends on the meters<\/strong> (connections\/ops\/storage) and your region. Use the pricing calculator and test with realistic load.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Example production cost considerations (what to model)<\/h3>\n\n\n\n<p>For production, estimate:\n&#8211; peak concurrent users per document,\n&#8211; average session duration,\n&#8211; operations per second per user (especially presence\/cursor),\n&#8211; document count and retention,\n&#8211; expected geographic distribution,\n&#8211; token service request rate (tokens per user\/session).<\/p>\n\n\n\n<p>Then validate with:\n&#8211; a staged load test,\n&#8211; real telemetry once in pilot.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">10. Step-by-Step Hands-On Tutorial<\/h2>\n\n\n\n<p>This lab builds a small collaborative Web app: a <strong>shared counter<\/strong> that updates live across multiple browser tabs\/windows using <strong>Azure Fluid Relay<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Objective<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Provision Azure Fluid Relay in Azure.<\/li>\n<li>Create a tenant and obtain the connection endpoint details.<\/li>\n<li>Build:<\/li>\n<li>a small <strong>token service<\/strong> (Node.js\/Express) that mints Fluid access tokens, and<\/li>\n<li>a <strong>web client<\/strong> (Vite) that creates\/joins a Fluid container using a shared map.<\/li>\n<li>Verify real-time sync across two browsers.<\/li>\n<li>Clean up Azure resources.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Lab Overview<\/h3>\n\n\n\n<p>You will create two local projects:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>token-server\/<\/code> \u2013 holds the tenant secret and issues short-lived tokens (so you don\u2019t place secrets in the browser).<\/li>\n<li><code>web-client\/<\/code> \u2013 the SPA that connects to Azure Fluid Relay using the token it fetches from <code>token-server<\/code>.<\/li>\n<\/ul>\n\n\n\n<blockquote>\n<p>Why a token server? In production, you should not embed relay tenant keys in a browser bundle. A token server is the standard pattern to keep secrets server-side.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Create an Azure Fluid Relay resource<\/h3>\n\n\n\n<p><strong>Goal:<\/strong> Provision Azure Fluid Relay in your Azure subscription.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>In the Azure portal, search for <strong>Azure Fluid Relay<\/strong> and select it.<\/li>\n<li>Click <strong>Create<\/strong>.<\/li>\n<li>Choose:\n   &#8211; Subscription\n   &#8211; Resource group (create new, e.g., <code>rg-fluid-lab<\/code>)\n   &#8211; Resource name (e.g., <code>afr-fluidlab-&lt;unique&gt;<\/code>)\n   &#8211; Region (choose close to you)<\/li>\n<li>Create the resource.<\/li>\n<\/ol>\n\n\n\n<p><strong>Expected outcome:<\/strong> An Azure Fluid Relay resource exists in your resource group.<\/p>\n\n\n\n<p><strong>Verify:<\/strong>\n&#8211; In the portal, open the resource and confirm it shows a valid <strong>endpoint\/host<\/strong> (exact UI fields may differ by portal updates).<\/p>\n\n\n\n<blockquote>\n<p>If you don\u2019t see Azure Fluid Relay in the portal, the service may be restricted in your subscription\/region. Verify availability in the official docs.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Create a tenant and collect connection details<\/h3>\n\n\n\n<p><strong>Goal:<\/strong> Create a tenant and gather the tenant ID, endpoint, and a key\/secret required for token signing (exact model varies\u2014follow the current portal\/docs).<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>In the Azure Fluid Relay resource blade, find <strong>Tenants<\/strong> (or similar).<\/li>\n<li>Create a tenant (name it <code>lab-tenant<\/code>).<\/li>\n<li>Capture these values (names may differ slightly):\n   &#8211; <strong>Tenant ID<\/strong>\n   &#8211; <strong>Service endpoint<\/strong> (URL\/host)\n   &#8211; <strong>Tenant key \/ primary key<\/strong> (or equivalent secret used to sign tokens)<\/li>\n<\/ol>\n\n\n\n<p>Store them securely for the lab.<\/p>\n\n\n\n<p><strong>Expected outcome:<\/strong> You have the tenant identifiers and secret required for the token server.<\/p>\n\n\n\n<p><strong>Verify:<\/strong>\n&#8211; Tenant appears in the Azure portal list.\n&#8211; You can view\/copy the endpoint and tenant ID.<\/p>\n\n\n\n<blockquote>\n<p>Security note: Treat the tenant key like a password. In production, store it in Key Vault and never ship it to browsers.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Set up the token server (Node.js\/Express)<\/h3>\n\n\n\n<p><strong>Goal:<\/strong> Create a local API that mints Fluid tokens for the web client.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">3.1 Create the project<\/h4>\n\n\n\n<pre><code class=\"language-bash\">mkdir azure-fluid-relay-lab\ncd azure-fluid-relay-lab\n\nmkdir token-server\ncd token-server\nnpm init -y\nnpm install express cors dotenv\nnpm install @fluidframework\/insecure-token-provider\n<\/code><\/pre>\n\n\n\n<p>Create a <code>.env<\/code> file in <code>token-server\/<\/code>:<\/p>\n\n\n\n<pre><code class=\"language-bash\">touch .env\n<\/code><\/pre>\n\n\n\n<p>Add the following (replace with your values):<\/p>\n\n\n\n<pre><code class=\"language-text\">PORT=7071\nFLUID_TENANT_ID=&lt;your-tenant-id&gt;\nFLUID_TENANT_KEY=&lt;your-tenant-key&gt;\nFLUID_ORDERER_URL=&lt;your-fluid-relay-endpoint&gt;\nFLUID_STORAGE_URL=&lt;your-fluid-relay-endpoint&gt;\n<\/code><\/pre>\n\n\n\n<blockquote>\n<p>Why <code>ORDERER_URL<\/code> and <code>STORAGE_URL<\/code> both equal the endpoint? Many Fluid service configs use separate orderer\/storage endpoints, but Azure Fluid Relay commonly uses a single endpoint host for both concepts. Confirm the required configuration shape in the official Azure Fluid Relay + <code>@fluidframework\/azure-client<\/code> documentation and samples.<\/p>\n<\/blockquote>\n\n\n\n<h4 class=\"wp-block-heading\">3.2 Implement the server<\/h4>\n\n\n\n<p>Create <code>index.js<\/code>:<\/p>\n\n\n\n<pre><code class=\"language-js\">import express from \"express\";\nimport cors from \"cors\";\nimport dotenv from \"dotenv\";\nimport { InsecureTokenProvider } from \"@fluidframework\/insecure-token-provider\";\n\ndotenv.config();\n\nconst app = express();\napp.use(cors());\napp.use(express.json());\n\nconst {\n  PORT = 7071,\n  FLUID_TENANT_ID,\n  FLUID_TENANT_KEY,\n} = process.env;\n\nif (!FLUID_TENANT_ID || !FLUID_TENANT_KEY) {\n  throw new Error(\"Missing FLUID_TENANT_ID or FLUID_TENANT_KEY in .env\");\n}\n\nconst tokenProvider = new InsecureTokenProvider(FLUID_TENANT_KEY, {\n  id: \"local-user\",\n  name: \"Local User\",\n});\n\n\/\/ Simple endpoint: returns a token for a given document\/container.\n\/\/ In real apps, you would authenticate the caller and generate a user identity per session.\napp.get(\"\/api\/fluid-token\", async (req, res) =&gt; {\n  const documentId = req.query.documentId?.toString();\n  if (!documentId) return res.status(400).json({ error: \"documentId is required\" });\n\n  \/\/ Scopes determine what the token can do. The exact scope strings are Fluid-specific.\n  \/\/ Keep this aligned to official Fluid\/Azure docs and samples.\n  const token = tokenProvider.fetchOrdererToken(\n    FLUID_TENANT_ID,\n    documentId\n  );\n\n  res.json({\n    tenantId: FLUID_TENANT_ID,\n    documentId,\n    \/\/ This returns a token appropriate for connecting to the service.\n    \/\/ Depending on the client library version, you may need to mint separate tokens\n    \/\/ or return a different structure. Verify with current docs.\n    token,\n  });\n});\n\napp.get(\"\/healthz\", (req, res) =&gt; res.send(\"ok\"));\n\napp.listen(PORT, () =&gt; {\n  console.log(`Token server listening on http:\/\/localhost:${PORT}`);\n});\n<\/code><\/pre>\n\n\n\n<p>Update <code>package.json<\/code> to enable ES modules:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  \"name\": \"token-server\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"start\": \"node index.js\"\n  }\n}\n<\/code><\/pre>\n\n\n\n<p>Start the token server:<\/p>\n\n\n\n<pre><code class=\"language-bash\">npm start\n<\/code><\/pre>\n\n\n\n<p><strong>Expected outcome:<\/strong> Server runs at <code>http:\/\/localhost:7071<\/code>.<\/p>\n\n\n\n<p><strong>Verify:<\/strong>\n&#8211; Open <code>http:\/\/localhost:7071\/healthz<\/code> \u2192 <code>ok<\/code><\/p>\n\n\n\n<blockquote>\n<p>Important: <code>InsecureTokenProvider<\/code> is intended for development\/testing. For production, implement a secure token service and follow current Azure Fluid Relay guidance (often involving a server-side signing key and authenticated users). The correct approach can evolve\u2014verify in official docs.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">Step 4: Create the web client (Vite)<\/h3>\n\n\n\n<p><strong>Goal:<\/strong> Create a browser app that creates\/joins a Fluid container and syncs a counter via <code>SharedMap<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">4.1 Scaffold the app<\/h4>\n\n\n\n<p>From <code>azure-fluid-relay-lab\/<\/code>:<\/p>\n\n\n\n<pre><code class=\"language-bash\">cd ..\nnpm create vite@latest web-client -- --template vanilla\ncd web-client\nnpm install\nnpm install @fluidframework\/azure-client fluid-framework\n<\/code><\/pre>\n\n\n\n<p>Create a <code>.env<\/code> file in <code>web-client\/<\/code>:<\/p>\n\n\n\n<pre><code class=\"language-text\">VITE_TOKEN_SERVER_BASEURL=http:\/\/localhost:7071\nVITE_FLUID_RELAY_ENDPOINT=&lt;your-fluid-relay-endpoint&gt;\n<\/code><\/pre>\n\n\n\n<blockquote>\n<p>The endpoint is used by the client to connect, but the token is fetched from your token server.<\/p>\n<\/blockquote>\n\n\n\n<h4 class=\"wp-block-heading\">4.2 Implement the Fluid client logic<\/h4>\n\n\n\n<p>Replace <code>web-client\/src\/main.js<\/code> with:<\/p>\n\n\n\n<pre><code class=\"language-js\">import \".\/style.css\";\nimport { AzureClient } from \"@fluidframework\/azure-client\";\nimport { SharedMap } from \"fluid-framework\";\n\nconst tokenServer = import.meta.env.VITE_TOKEN_SERVER_BASEURL;\nconst relayEndpoint = import.meta.env.VITE_FLUID_RELAY_ENDPOINT;\n\nif (!tokenServer || !relayEndpoint) {\n  throw new Error(\"Missing VITE_TOKEN_SERVER_BASEURL or VITE_FLUID_RELAY_ENDPOINT\");\n}\n\n\/\/ Very small UI\ndocument.querySelector(\"#app\").innerHTML = `\n  &lt;div style=\"font-family: system-ui; max-width: 720px; margin: 40px auto;\"&gt;\n    &lt;h1&gt;Azure Fluid Relay: Shared Counter&lt;\/h1&gt;\n    &lt;p&gt;\n      Container ID: &lt;code id=\"containerId\"&gt;(not connected)&lt;\/code&gt;\n    &lt;\/p&gt;\n    &lt;div style=\"display:flex; gap:12px; align-items:center;\"&gt;\n      &lt;button id=\"dec\"&gt;-&lt;\/button&gt;\n      &lt;div style=\"font-size: 24px;\"&gt;Count: &lt;span id=\"count\"&gt;0&lt;\/span&gt;&lt;\/div&gt;\n      &lt;button id=\"inc\"&gt;+&lt;\/button&gt;\n    &lt;\/div&gt;\n    &lt;p style=\"margin-top: 16px;\"&gt;\n      Tip: Open this page in two tabs. Both should stay in sync.\n    &lt;\/p&gt;\n  &lt;\/div&gt;\n`;\n\nconst containerIdEl = document.getElementById(\"containerId\");\nconst countEl = document.getElementById(\"count\");\nconst incBtn = document.getElementById(\"inc\");\nconst decBtn = document.getElementById(\"dec\");\n\n\/\/ Container schema: a single SharedMap named \"app\"\nconst containerSchema = {\n  initialObjects: {\n    app: SharedMap,\n  },\n};\n\nfunction getContainerIdFromUrl() {\n  const url = new URL(window.location.href);\n  return url.searchParams.get(\"containerId\");\n}\n\nfunction setContainerIdInUrl(containerId) {\n  const url = new URL(window.location.href);\n  url.searchParams.set(\"containerId\", containerId);\n  window.history.replaceState({}, \"\", url.toString());\n}\n\nasync function fetchFluidToken(documentId) {\n  const url = new URL(`${tokenServer}\/api\/fluid-token`);\n  url.searchParams.set(\"documentId\", documentId);\n\n  const res = await fetch(url.toString());\n  if (!res.ok) throw new Error(`Token server error: ${res.status}`);\n  return res.json();\n}\n\nasync function main() {\n  \/\/ Use an existing container ID from the URL, or create a new one.\n  let containerId = getContainerIdFromUrl();\n\n  \/\/ The \"documentId\" is used when minting tokens. For this lab, we align it with containerId.\n  \/\/ When creating a new container, we temporarily use a placeholder ID for token fetch, then update.\n  let documentId = containerId ?? \"new\";\n\n  const tokenResponse = await fetchFluidToken(documentId);\n\n  \/\/ AzureClient config shape can vary by package version; align with official docs\/samples.\n  const client = new AzureClient({\n    connection: {\n      tenantId: tokenResponse.tenantId,\n      tokenProvider: {\n        \/\/ AzureClient expects a token provider interface; some samples provide a function.\n        \/\/ If this doesn\u2019t match your installed version, use the official sample code for your SDK version.\n        fetchOrdererToken: async () =&gt; tokenResponse.token,\n        fetchStorageToken: async () =&gt; tokenResponse.token,\n      },\n      endpoints: {\n        orderer: relayEndpoint,\n        storage: relayEndpoint,\n      },\n    },\n  });\n\n  let container;\n  let services;\n\n  if (!containerId) {\n    \/\/ Create new container\n    ({ container, services } = await client.createContainer(containerSchema));\n    containerId = await container.attach();\n    setContainerIdInUrl(containerId);\n\n    \/\/ Now that we have a real containerId, we should ideally fetch a token bound to it.\n    \/\/ For simplicity in this lab, we continue; production implementations should ensure\n    \/\/ tokens are minted for the actual container\/document ID.\n  } else {\n    \/\/ Join existing container\n    ({ container, services } = await client.getContainer(containerId, containerSchema));\n  }\n\n  containerIdEl.textContent = containerId;\n\n  const appMap = container.initialObjects.app;\n\n  \/\/ Initialize default value\n  if (!appMap.has(\"count\")) appMap.set(\"count\", 0);\n\n  const render = () =&gt; {\n    countEl.textContent = String(appMap.get(\"count\"));\n  };\n\n  \/\/ React to remote updates\n  appMap.on(\"valueChanged\", (changed) =&gt; {\n    if (changed.key === \"count\") render();\n  });\n\n  \/\/ Local updates\n  incBtn.onclick = () =&gt; appMap.set(\"count\", (appMap.get(\"count\") ?? 0) + 1);\n  decBtn.onclick = () =&gt; appMap.set(\"count\", (appMap.get(\"count\") ?? 0) - 1);\n\n  render();\n}\n\nmain().catch((err) =&gt; {\n  console.error(err);\n  containerIdEl.textContent = \"ERROR (check console)\";\n});\n<\/code><\/pre>\n\n\n\n<p>Start the web client:<\/p>\n\n\n\n<pre><code class=\"language-bash\">npm run dev\n<\/code><\/pre>\n\n\n\n<p><strong>Expected outcome:<\/strong> Vite prints a local URL (for example <code>http:\/\/localhost:5173<\/code>).<\/p>\n\n\n\n<p><strong>Verify:<\/strong>\n&#8211; Open the URL; you should see the counter UI.\n&#8211; Copy the full URL (it will gain a <code>?containerId=...<\/code> parameter after first load).<\/p>\n\n\n\n<hr class=\"wp-block-separator\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">Step 5: Validate collaboration with two clients<\/h3>\n\n\n\n<p><strong>Goal:<\/strong> Prove that state changes synchronize in real time.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open the app URL in <strong>Tab A<\/strong>.<\/li>\n<li>Open the same URL in <strong>Tab B<\/strong> (or a different browser).<\/li>\n<li>Click <strong>+<\/strong> or <strong>&#8211;<\/strong> in Tab A and watch Tab B update.<\/li>\n<\/ol>\n\n\n\n<p><strong>Expected outcome:<\/strong> The count stays consistent across tabs within a fraction of a second.<\/p>\n\n\n\n<hr class=\"wp-block-separator\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">Validation<\/h3>\n\n\n\n<p>Use this checklist:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Token server is healthy: <code>GET \/healthz<\/code> returns <code>ok<\/code><\/li>\n<li>Web app loads and displays a <strong>Container ID<\/strong><\/li>\n<li>Two browser sessions see the <strong>same Container ID<\/strong> and the same counter value<\/li>\n<li>Updates propagate both directions<\/li>\n<\/ul>\n\n\n\n<p>If the container ID doesn\u2019t appear, open DevTools Console and check the errors.<\/p>\n\n\n\n<hr class=\"wp-block-separator\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">Troubleshooting<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">Issue: \u201cToken server error: 400 documentId is required\u201d<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ensure the client calls <code>\/api\/fluid-token?documentId=...<\/code>.<\/li>\n<li>Ensure <code>VITE_TOKEN_SERVER_BASEURL<\/code> is correct.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Issue: Authentication \/ token errors when connecting<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Confirm your tenant ID and tenant key are correct.<\/li>\n<li>Ensure you are using the correct endpoint URL (from the Azure Fluid Relay resource).<\/li>\n<li>Token provider interfaces vary by SDK version; if you see runtime errors about token provider shape, align your code with the official sample for your installed <code>@fluidframework\/azure-client<\/code> version.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Issue: CORS errors in browser<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Confirm <code>cors()<\/code> middleware is enabled in token server.<\/li>\n<li>If you changed ports, update <code>VITE_TOKEN_SERVER_BASEURL<\/code>.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Issue: Two tabs don\u2019t sync<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Confirm both tabs have the exact same URL including <code>containerId<\/code>.<\/li>\n<li>Confirm there are no errors in console.<\/li>\n<li>Ensure both clients can reach the Azure Fluid Relay endpoint (corporate proxies sometimes block websockets).<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Issue: Package\/API mismatch<\/h4>\n\n\n\n<p>Fluid Framework packages evolve. If your <code>AzureClient<\/code> configuration fails:\n&#8211; Check the installed versions: <code>npm ls @fluidframework\/azure-client fluid-framework<\/code>\n&#8211; Use the official docs and samples for the exact version you installed.\n&#8211; Start from Microsoft\u2019s official sample and only then adapt.<\/p>\n\n\n\n<hr class=\"wp-block-separator\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">Cleanup<\/h3>\n\n\n\n<p>To avoid ongoing charges:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\n<p>Stop local processes:\n   &#8211; Token server: <code>Ctrl+C<\/code>\n   &#8211; Vite dev server: <code>Ctrl+C<\/code><\/p>\n<\/li>\n<li>\n<p>Delete Azure resources:\n   &#8211; In Azure portal, delete the resource group <code>rg-fluid-lab<\/code> (or whatever you created).<\/p>\n<\/li>\n<\/ol>\n\n\n\n<p><strong>Expected outcome:<\/strong> Azure Fluid Relay resource (and any related resources) are removed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">11. Best Practices<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Architecture best practices<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Use a token service<\/strong>: keep relay tenant secrets out of the browser.<\/li>\n<li><strong>Model your data structures intentionally<\/strong>:<\/li>\n<li>Store shared state as small, incremental updates.<\/li>\n<li>Keep large objects\/blobs out of Fluid DDS; store in Blob Storage and share references\/IDs.<\/li>\n<li><strong>Partition collaboration domains<\/strong>:<\/li>\n<li>Use separate tenants for dev\/test\/prod.<\/li>\n<li>For SaaS, decide how you map customers to tenants\/containers.<\/li>\n<li><strong>Plan for reconnects<\/strong>: clients will drop and reconnect; design UI for \u201creconnecting\u2026\u201d states.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">IAM\/security best practices<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Least privilege<\/strong> on Azure resource management (RBAC).<\/li>\n<li>Keep tenant keys in <strong>Key Vault<\/strong> and rotate them.<\/li>\n<li>Authenticate end users (Entra ID or equivalent) and issue tokens based on app authorization rules.<\/li>\n<li>Use short-lived tokens and restrict scopes appropriately (verify available scopes in current docs).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Cost best practices<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Rate-limit presence\/cursor updates.<\/li>\n<li>Avoid high-frequency writes for non-critical UI effects.<\/li>\n<li>Implement document retention and cleanup policies.<\/li>\n<li>Separate dev\/test environments to avoid accidental production-scale usage.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Performance best practices<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Reduce operation payload size.<\/li>\n<li>Batch changes when possible (careful: batching affects UX).<\/li>\n<li>Avoid \u201cchatty\u201d updates (e.g., writing on every keystroke for non-text use cases).<\/li>\n<li>Choose the closest region to the majority of your users.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Reliability best practices<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use retries with exponential backoff on token acquisition and relay connection (client-side).<\/li>\n<li>Design for partial failures (token service down, relay temporarily unavailable, network issues).<\/li>\n<li>Keep token service stateless and horizontally scalable.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Operations best practices<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Instrument token service with:<\/li>\n<li>request IDs<\/li>\n<li>latency histograms<\/li>\n<li>auth failure metrics<\/li>\n<li>Track:<\/li>\n<li>token issuance rate<\/li>\n<li>container creation rate<\/li>\n<li>per-document active users (in your app telemetry)<\/li>\n<li>Use consistent tagging: <code>env<\/code>, <code>app<\/code>, <code>owner<\/code>, <code>costCenter<\/code>, <code>dataClassification<\/code>.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Governance\/tagging\/naming best practices<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Naming example:<\/li>\n<li>Resource group: <code>rg-&lt;app&gt;-&lt;env&gt;-&lt;region&gt;<\/code><\/li>\n<li>Relay: <code>afr-&lt;app&gt;-&lt;env&gt;-&lt;region&gt;-&lt;unique&gt;<\/code><\/li>\n<li>Apply resource locks on production resources where appropriate.<\/li>\n<li>Use Azure Policy to enforce tags and allowed regions.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">12. Security Considerations<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Identity and access model<\/h3>\n\n\n\n<p>Think in two layers:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\n<p><strong>Management plane<\/strong> (Azure portal\/ARM):\n   &#8211; Controlled with Azure RBAC.\n   &#8211; Limit who can create tenants\/keys and read secrets.<\/p>\n<\/li>\n<li>\n<p><strong>Data plane<\/strong> (client connections to relay):\n   &#8211; Controlled by tokens used by Fluid clients.\n   &#8211; Recommended: a server-side token service that:<\/p>\n<ul>\n<li>authenticates the user,<\/li>\n<li>authorizes access to a document\/container,<\/li>\n<li>issues a short-lived token.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Encryption<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>In transit: clients connect over TLS (HTTPS\/WSS) to Azure endpoints.<\/li>\n<li>At rest: Azure services typically encrypt data at rest by default; confirm Azure Fluid Relay\u2019s at-rest encryption guarantees and customer-managed key (CMK) support (if required) in official docs.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Network exposure<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Most deployments use public endpoints. Protect access by:<\/li>\n<li>issuing tokens only to authorized users,<\/li>\n<li>limiting token lifetime,<\/li>\n<li>validating container\/document membership in your token service.<\/li>\n<li>If your compliance requires private connectivity, verify whether Azure Fluid Relay supports Private Link or other private access patterns.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Secrets handling<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Do not store tenant keys in:<\/li>\n<li>browser code,<\/li>\n<li>mobile app binaries,<\/li>\n<li>public CI logs,<\/li>\n<li>developer wikis.<\/li>\n<li>Store keys in Key Vault and access them from the token service using managed identity where possible.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Audit\/logging<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Log token issuance events:<\/li>\n<li>user ID<\/li>\n<li>container\/document ID<\/li>\n<li>scopes granted<\/li>\n<li>timestamp<\/li>\n<li>request correlation ID<\/li>\n<li>Avoid logging the full token or secrets.<\/li>\n<li>If Azure Fluid Relay emits diagnostic logs, route them to Log Analytics and set retention appropriately (verify capabilities).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Compliance considerations<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Confirm data residency (region) and compliance certifications relevant to your org.<\/li>\n<li>For regulated data (HIPAA\/PCI\/etc.), ensure you understand what is stored in relay snapshots\/summaries and whether you should avoid placing regulated content in Fluid state.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Common security mistakes<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Embedding tenant keys in SPAs.<\/li>\n<li>Issuing tokens without authenticating the user.<\/li>\n<li>Using long-lived tokens.<\/li>\n<li>Over-granting scopes (everyone can write everywhere).<\/li>\n<li>Allowing untrusted users to create unlimited containers (DoS risk).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Secure deployment recommendations<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Token service behind an authenticated gateway (Front Door + WAF, API Management, or App Service auth).<\/li>\n<li>Rate-limit token endpoint and require user auth.<\/li>\n<li>Implement allow-lists for which containers a user can access.<\/li>\n<li>Rotate keys and implement incident response playbooks for key compromise.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">13. Limitations and Gotchas<\/h2>\n\n\n\n<blockquote>\n<p>Always confirm current limits in official docs, because collaboration services evolve and quotas can change.<\/p>\n<\/blockquote>\n\n\n\n<p>Common limitations and \u201cgotchas\u201d to plan for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Region availability is not universal.<\/strong> Some subscriptions\/regions may not support Azure Fluid Relay.<\/li>\n<li><strong>SDK\/API version drift.<\/strong> Fluid packages change; samples may target specific versions. Pin versions and align with official samples.<\/li>\n<li><strong>Token provider interfaces vary by version.<\/strong> The shape expected by <code>AzureClient<\/code> can change; follow current docs.<\/li>\n<li><strong>Secrets in browser is a hard \u201cno\u201d<\/strong> for production. A token service is required for secure deployments.<\/li>\n<li><strong>High-frequency updates can explode costs<\/strong> (presence\/cursor, rapid-fire updates).<\/li>\n<li><strong>Websocket\/proxy restrictions.<\/strong> Corporate networks may block websockets or throttle long-lived connections.<\/li>\n<li><strong>Document lifecycle management is on you.<\/strong> Decide retention, deletion, and archival strategies.<\/li>\n<li><strong>Multi-region active-active is non-trivial.<\/strong> If the relay is regional, global user bases must trade off latency vs architecture complexity. Validate DR\/BCP expectations with the service\u2019s capabilities (verify).<\/li>\n<li><strong>Data modeling pitfalls.<\/strong> Storing large objects in shared structures can degrade performance and increase operation volume.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">14. Comparison with Alternatives<\/h2>\n\n\n\n<p>Azure Fluid Relay is specialized: real-time <strong>shared state collaboration<\/strong> via Fluid Framework. Alternatives vary depending on what you really need.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Comparison table<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Option<\/th>\n<th>Best For<\/th>\n<th>Strengths<\/th>\n<th>Weaknesses<\/th>\n<th>When to Choose<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Azure Fluid Relay<\/strong><\/td>\n<td>Real-time co-authoring and shared state collaboration<\/td>\n<td>Managed Fluid backend; DDS-based collaboration; purpose-built for collaboration correctness<\/td>\n<td>Requires Fluid data modeling; token service needed; service availability\/quotas<\/td>\n<td>You need Fluid-style collaboration (documents\/containers) in a web app<\/td>\n<\/tr>\n<tr>\n<td><strong>Azure Web PubSub<\/strong><\/td>\n<td>Pub\/sub messaging to web clients<\/td>\n<td>Simple real-time messaging; supports websockets; good for chat, notifications, live feeds<\/td>\n<td>You design state convergence; no built-in co-authoring model<\/td>\n<td>You need broadcast\/messaging rather than shared state collaboration<\/td>\n<\/tr>\n<tr>\n<td><strong>Azure SignalR Service<\/strong><\/td>\n<td>Real-time messaging for apps (often ASP.NET)<\/td>\n<td>Great for app real-time updates; integrates with ASP.NET<\/td>\n<td>Not a collaboration engine; you build conflict resolution<\/td>\n<td>You already use SignalR patterns and need \u201cpush\u201d updates<\/td>\n<\/tr>\n<tr>\n<td><strong>Microsoft 365 Live Share (Teams)<\/strong><\/td>\n<td>Collaboration inside Microsoft Teams meetings<\/td>\n<td>Tight integration with Teams; meeting context and presence<\/td>\n<td>Primarily Teams-centric; not a general Azure service<\/td>\n<td>Your app runs in Teams and you want in-meeting collaboration<\/td>\n<\/tr>\n<tr>\n<td><strong>Self-host Fluid service components<\/strong> (e.g., on AKS)<\/td>\n<td>Full control, custom compliance<\/td>\n<td>Full control over infra and data; custom networking<\/td>\n<td>High ops burden; scaling and correctness complexity<\/td>\n<td>You must run privately\/on-prem or need deep customization<\/td>\n<\/tr>\n<tr>\n<td><strong>Firebase Realtime Database \/ Firestore<\/strong><\/td>\n<td>Mobile\/web real-time sync with managed DB<\/td>\n<td>Very easy dev experience; client SDKs; real-time listeners<\/td>\n<td>Collaboration semantics differ; ordering\/convergence is your design<\/td>\n<td>You want real-time data sync but not Fluid-style co-authoring<\/td>\n<\/tr>\n<tr>\n<td><strong>Yjs + WebSocket server<\/strong> (self-managed)<\/td>\n<td>Open-source collaborative text\/CRDT apps<\/td>\n<td>Excellent CRDT tooling; flexible<\/td>\n<td>You run servers; security, scaling, persistence are on you<\/td>\n<td>You want CRDT collaboration with full control and can operate it<\/td>\n<\/tr>\n<tr>\n<td><strong>Socket.IO + Redis<\/strong> (self-managed)<\/td>\n<td>Custom real-time apps<\/td>\n<td>Flexible, widely known<\/td>\n<td>Must build collaboration semantics; ops burden<\/td>\n<td>You need generic realtime and accept building the hard parts<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">15. Real-World Example<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Enterprise example: Incident collaboration dashboard for SRE\/Operations<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> A large enterprise runs a 24\/7 operations center. During incidents, multiple responders need a shared, live dashboard to coordinate owners, mitigations, and timelines. Existing dashboards are read-only and updates are slow and fragmented across chat messages.<\/li>\n<li><strong>Proposed architecture:<\/strong><\/li>\n<li>SPA hosted on Azure (App Service or Static Web Apps)<\/li>\n<li>Token service on Azure Functions<\/li>\n<li>Azure Fluid Relay for the live \u201cincident room\u201d container per incident ID<\/li>\n<li>Key Vault for relay tenant keys<\/li>\n<li>Application Insights + Azure Monitor for telemetry<\/li>\n<li><strong>Why Azure Fluid Relay was chosen:<\/strong><\/li>\n<li>Shared state model fits \u201cone incident room, many editors\u201d<\/li>\n<li>Avoids running custom websocket infrastructure<\/li>\n<li>Tight integration with Azure governance and monitoring<\/li>\n<li><strong>Expected outcomes:<\/strong><\/li>\n<li>Reduced coordination lag<\/li>\n<li>Fewer conflicting updates<\/li>\n<li>Better auditability via app-side logging of state changes (who changed what)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Startup\/small-team example: Collaborative product roadmap tool<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> A startup wants a simple collaborative roadmap tool where founders and early customers can propose items and prioritize in real time. They have limited DevOps capacity.<\/li>\n<li><strong>Proposed architecture:<\/strong><\/li>\n<li>Frontend: React (Vercel or Azure Static Web Apps)<\/li>\n<li>Token service: minimal Node API on Azure Container Apps<\/li>\n<li>Azure Fluid Relay for per-workspace shared board state<\/li>\n<li>Postgres (optional) for long-term storage and exporting snapshots<\/li>\n<li><strong>Why Azure Fluid Relay was chosen:<\/strong><\/li>\n<li>Fast path to real-time collaboration without building a custom sync engine<\/li>\n<li>Usage-based costs are easier to align with early-stage growth (validate with pricing)<\/li>\n<li><strong>Expected outcomes:<\/strong><\/li>\n<li>MVP shipped faster<\/li>\n<li>Collaboration features become a differentiator without heavy ops overhead<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">16. FAQ<\/h2>\n\n\n\n<p>1) <strong>What is Azure Fluid Relay used for?<\/strong><br\/>\nIt\u2019s used to enable real-time, multi-user collaboration in applications built with the Fluid Framework\u2014such as co-editing, shared boards, and live dashboards.<\/p>\n\n\n\n<p>2) <strong>Is Azure Fluid Relay the same as Azure Relay?<\/strong><br\/>\nNo. Azure Relay is for hybrid connectivity. Azure Fluid Relay is for real-time collaboration (Fluid Framework containers).<\/p>\n\n\n\n<p>3) <strong>Do I need the Fluid Framework to use Azure Fluid Relay?<\/strong><br\/>\nPractically, yes. Azure Fluid Relay is designed as the managed collaboration service for Fluid clients and containers.<\/p>\n\n\n\n<p>4) <strong>Can I use Azure Fluid Relay for chat apps?<\/strong><br\/>\nYou can, but it\u2019s usually not the simplest option. For chat\/broadcast messaging, Azure Web PubSub or Azure SignalR Service are often a better fit.<\/p>\n\n\n\n<p>5) <strong>Do I need a backend service?<\/strong><br\/>\nFor production security, yes\u2014you should use a token service so you don\u2019t put tenant secrets in the browser. For prototypes, you can sometimes use insecure\/dev patterns, but avoid them in real deployments.<\/p>\n\n\n\n<p>6) <strong>How do users join the same collaboration session?<\/strong><br\/>\nTypically through a shared container\/document ID embedded in a URL. Your app mints tokens for that container ID and users join using the same identifier.<\/p>\n\n\n\n<p>7) <strong>How do I control who can access a container?<\/strong><br\/>\nYour token service is the control point. Authenticate the user and only mint tokens for containers they\u2019re authorized to join.<\/p>\n\n\n\n<p>8) <strong>Does Azure Fluid Relay store data permanently?<\/strong><br\/>\nFluid collaboration typically persists summaries\/snapshots so documents can be reloaded. What is stored and for how long depends on service behavior and your application design. Verify specifics in docs.<\/p>\n\n\n\n<p>9) <strong>Can I delete containers\/documents?<\/strong><br\/>\nDeletion and lifecycle management depend on the service APIs and your architecture. Verify supported deletion capabilities and recommended cleanup patterns in official docs.<\/p>\n\n\n\n<p>10) <strong>How do I handle presence (who\u2019s online)?<\/strong><br\/>\nYou can model presence in shared state, but presence updates can be high-frequency. Throttle updates and avoid excessive operation volume.<\/p>\n\n\n\n<p>11) <strong>What happens if a client disconnects?<\/strong><br\/>\nClients should reconnect and resynchronize. Design the UI to handle reconnection gracefully and avoid data loss assumptions.<\/p>\n\n\n\n<p>12) <strong>Does Azure Fluid Relay support private networking?<\/strong><br\/>\nThis depends on current service features. Verify Private Link\/VNet support (if any) in official docs.<\/p>\n\n\n\n<p>13) <strong>How do I monitor Azure Fluid Relay?<\/strong><br\/>\nMonitor your token service and app with Application Insights\/Azure Monitor. For relay-specific metrics\/logs, verify what the service exposes and enable diagnostic settings if available.<\/p>\n\n\n\n<p>14) <strong>Is Azure Fluid Relay suitable for global, low-latency collaboration?<\/strong><br\/>\nIt can be, but it\u2019s regional. Global performance depends on region selection and user distribution. For very global scenarios, test latency and consider architecture patterns carefully.<\/p>\n\n\n\n<p>15) <strong>What are the biggest cost risks?<\/strong><br\/>\nHigh-frequency operations (presence\/cursor\/rapid updates), high concurrency, and excessive logging. Model usage early and throttle noisy updates.<\/p>\n\n\n\n<p>16) <strong>Can I use it with React\/Angular\/Vue?<\/strong><br\/>\nYes. Fluid clients run in the browser; the framework choice is mostly about how you build UI.<\/p>\n\n\n\n<p>17) <strong>Do I need to use Entra ID?<\/strong><br\/>\nNot strictly, but Entra ID is commonly used for user authentication. Regardless of IdP, you should authenticate users and authorize container access in your token service.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">17. Top Online Resources to Learn Azure Fluid Relay<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Resource Type<\/th>\n<th>Name<\/th>\n<th>Why It Is Useful<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Official documentation<\/td>\n<td>Azure Fluid Relay docs \u2013 https:\/\/learn.microsoft.com\/azure\/azure-fluid-relay\/<\/td>\n<td>Primary source for provisioning, auth patterns, limits, and SDK guidance<\/td>\n<\/tr>\n<tr>\n<td>Official pricing<\/td>\n<td>Azure Fluid Relay pricing \u2013 https:\/\/azure.microsoft.com\/pricing\/details\/fluid-relay\/<\/td>\n<td>Current meters, tiers, and regional pricing model<\/td>\n<\/tr>\n<tr>\n<td>Pricing calculator<\/td>\n<td>Azure Pricing Calculator \u2013 https:\/\/azure.microsoft.com\/pricing\/calculator\/<\/td>\n<td>Build scenario-based estimates including related services<\/td>\n<\/tr>\n<tr>\n<td>Open-source framework docs<\/td>\n<td>Fluid Framework docs \u2013 https:\/\/fluidframework.com\/<\/td>\n<td>Concepts (containers, DDS), client APIs, and collaboration patterns<\/td>\n<\/tr>\n<tr>\n<td>SDK reference<\/td>\n<td><code>@fluidframework\/azure-client<\/code> package (verify current docs via Microsoft links)<\/td>\n<td>Shows how to connect to Azure Fluid Relay from web apps<\/td>\n<\/tr>\n<tr>\n<td>Samples (official \/ trusted)<\/td>\n<td>Fluid Framework GitHub \u2013 https:\/\/github.com\/microsoft\/FluidFramework<\/td>\n<td>Source, examples, and issues; align your versions with samples<\/td>\n<\/tr>\n<tr>\n<td>Azure architecture guidance<\/td>\n<td>Azure Architecture Center \u2013 https:\/\/learn.microsoft.com\/azure\/architecture\/<\/td>\n<td>General Azure web architecture, identity, security, and ops patterns<\/td>\n<\/tr>\n<tr>\n<td>Identity guidance<\/td>\n<td>Microsoft Entra ID docs \u2013 https:\/\/learn.microsoft.com\/entra\/identity\/<\/td>\n<td>Implement secure user auth for your token service<\/td>\n<\/tr>\n<tr>\n<td>Video learning<\/td>\n<td>Microsoft Azure YouTube \u2013 https:\/\/www.youtube.com\/@MicrosoftAzure<\/td>\n<td>Look for Fluid\/real-time collaboration sessions (availability varies)<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">18. Training and Certification Providers<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Institute<\/th>\n<th>Suitable Audience<\/th>\n<th>Likely Learning Focus<\/th>\n<th>Mode<\/th>\n<th>Website URL<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>DevOpsSchool.com<\/td>\n<td>DevOps engineers, cloud engineers, architects<\/td>\n<td>Azure fundamentals, DevOps, cloud architecture practices<\/td>\n<td>Check website<\/td>\n<td>https:\/\/www.devopsschool.com\/<\/td>\n<\/tr>\n<tr>\n<td>ScmGalaxy.com<\/td>\n<td>Students, engineers, managers<\/td>\n<td>DevOps\/SCM training, CI\/CD and tooling foundations<\/td>\n<td>Check website<\/td>\n<td>https:\/\/www.scmgalaxy.com\/<\/td>\n<\/tr>\n<tr>\n<td>CLoudOpsNow.in<\/td>\n<td>Cloud ops and platform teams<\/td>\n<td>Cloud operations, reliability, monitoring and automation<\/td>\n<td>Check website<\/td>\n<td>https:\/\/www.cloudopsnow.in\/<\/td>\n<\/tr>\n<tr>\n<td>SreSchool.com<\/td>\n<td>SREs, reliability engineers<\/td>\n<td>SRE practices, SLIs\/SLOs, incident response<\/td>\n<td>Check website<\/td>\n<td>https:\/\/www.sreschool.com\/<\/td>\n<\/tr>\n<tr>\n<td>AiOpsSchool.com<\/td>\n<td>Ops\/DevOps teams exploring AIOps<\/td>\n<td>AIOps concepts, monitoring analytics and automation<\/td>\n<td>Check website<\/td>\n<td>https:\/\/www.aiopsschool.com\/<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">19. Top Trainers<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Platform\/Site<\/th>\n<th>Likely Specialization<\/th>\n<th>Suitable Audience<\/th>\n<th>Website URL<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>RajeshKumar.xyz<\/td>\n<td>DevOps\/cloud training content<\/td>\n<td>Beginners to intermediate engineers<\/td>\n<td>https:\/\/rajeshkumar.xyz\/<\/td>\n<\/tr>\n<tr>\n<td>devopstrainer.in<\/td>\n<td>DevOps coaching and workshops<\/td>\n<td>Engineers and teams<\/td>\n<td>https:\/\/www.devopstrainer.in\/<\/td>\n<\/tr>\n<tr>\n<td>devopsfreelancer.com<\/td>\n<td>Freelance DevOps guidance and services<\/td>\n<td>Small teams needing practical help<\/td>\n<td>https:\/\/www.devopsfreelancer.com\/<\/td>\n<\/tr>\n<tr>\n<td>devopssupport.in<\/td>\n<td>DevOps support and enablement<\/td>\n<td>Ops\/DevOps teams<\/td>\n<td>https:\/\/www.devopssupport.in\/<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">20. Top Consulting Companies<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Company<\/th>\n<th>Likely Service Area<\/th>\n<th>Where They May Help<\/th>\n<th>Consulting Use Case Examples<\/th>\n<th>Website URL<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>cotocus.com<\/td>\n<td>Cloud\/DevOps consulting<\/td>\n<td>Architecture, delivery, automation, ops enablement<\/td>\n<td>Designing token service + deployment pipelines; observability rollout<\/td>\n<td>https:\/\/cotocus.com\/<\/td>\n<\/tr>\n<tr>\n<td>DevOpsSchool.com<\/td>\n<td>DevOps and cloud consulting<\/td>\n<td>DevOps transformation, training + implementation support<\/td>\n<td>CI\/CD for web + token service; Azure governance setup<\/td>\n<td>https:\/\/www.devopsschool.com\/<\/td>\n<\/tr>\n<tr>\n<td>DEVOPSCONSULTING.IN<\/td>\n<td>DevOps consulting services<\/td>\n<td>Platform engineering, SRE practices, operational maturity<\/td>\n<td>Production readiness reviews; monitoring and incident response processes<\/td>\n<td>https:\/\/www.devopsconsulting.in\/<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">21. Career and Learning Roadmap<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">What to learn before Azure Fluid Relay<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Web fundamentals: HTTP, websockets, SPA basics<\/li>\n<li>JavaScript\/TypeScript (strongly recommended)<\/li>\n<li>Basic Azure concepts: subscriptions, resource groups, RBAC, Key Vault<\/li>\n<li>Identity basics: OAuth2\/OIDC, Entra ID app registrations<\/li>\n<li>API development basics (for token service)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">What to learn after Azure Fluid Relay<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Advanced Fluid Framework concepts:<\/li>\n<li>distributed data structures<\/li>\n<li>summarization and document lifecycle<\/li>\n<li>performance tuning patterns<\/li>\n<li>Security hardening:<\/li>\n<li>secure token service patterns<\/li>\n<li>secret rotation with Key Vault<\/li>\n<li>threat modeling for collaboration features<\/li>\n<li>Observability:<\/li>\n<li>Application Insights distributed tracing<\/li>\n<li>usage analytics (per-container and per-feature)<\/li>\n<li>Scaling patterns:<\/li>\n<li>rate limiting<\/li>\n<li>multi-region web frontends<\/li>\n<li>DR planning (depending on service capabilities\u2014verify)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Job roles that use it<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Frontend engineer building collaborative web apps<\/li>\n<li>Full-stack engineer building real-time products<\/li>\n<li>Cloud engineer\/architect designing web platform services<\/li>\n<li>SRE\/DevOps engineer operating token services and monitoring pipelines<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Certification path (if available)<\/h3>\n\n\n\n<p>Azure Fluid Relay does not typically map to a dedicated certification by itself. Relevant Azure certifications for the surrounding skills include:\n&#8211; Azure Developer Associate (AZ-204)\n&#8211; Azure Solutions Architect Expert (AZ-305)\n&#8211; Azure Security Engineer Associate (AZ-500)<\/p>\n\n\n\n<p>(Always verify current certification names and requirements on Microsoft Learn.)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Project ideas for practice<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Collaborative markdown editor with presence indicators<\/li>\n<li>Shared kanban board with drag-and-drop<\/li>\n<li>Live poll app with real-time results and admin controls<\/li>\n<li>Incident room dashboard template with roles (commander\/editor\/viewer)<\/li>\n<li>\u201cCo-design\u201d product configurator with shared state + chat (chat via Web PubSub; shared config via Fluid)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">22. Glossary<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Fluid Framework:<\/strong> Open-source Microsoft framework for building real-time collaborative applications using shared data structures.<\/li>\n<li><strong>Azure Fluid Relay:<\/strong> Azure managed service that provides the collaboration backend endpoint for Fluid Framework clients.<\/li>\n<li><strong>Container:<\/strong> A Fluid concept representing a collaborative document\/session that hosts shared objects.<\/li>\n<li><strong>Distributed Data Structure (DDS):<\/strong> Data structures (like shared maps\/strings) that synchronize across clients via operations.<\/li>\n<li><strong>Operation (op):<\/strong> A change event sent through the collaboration service to synchronize state.<\/li>\n<li><strong>Orderer:<\/strong> Collaboration service component concept that orders operations consistently (implementation abstracted in managed services).<\/li>\n<li><strong>Snapshot\/Summary:<\/strong> Persisted representation of the document state so it can be reloaded efficiently.<\/li>\n<li><strong>Tenant:<\/strong> Logical grouping in Azure Fluid Relay, used for scoping and credential separation.<\/li>\n<li><strong>Token service:<\/strong> Your backend API that issues short-lived access tokens for clients to connect to Azure Fluid Relay securely.<\/li>\n<li><strong>RBAC:<\/strong> Role-Based Access Control in Azure for management-plane permissions.<\/li>\n<li><strong>Entra ID:<\/strong> Microsoft identity platform (formerly Azure AD) used for authentication\/authorization.<\/li>\n<li><strong>WSS:<\/strong> Secure WebSocket protocol (WebSocket over TLS).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">23. Summary<\/h2>\n\n\n\n<p>Azure Fluid Relay is an Azure Web service that enables <strong>real-time collaborative experiences<\/strong> by providing a managed backend for the <strong>Fluid Framework<\/strong>. It\u2019s most valuable when you need <strong>shared state co-authoring<\/strong> (not just messaging) and want to avoid operating complex real-time infrastructure yourself.<\/p>\n\n\n\n<p>From an architecture standpoint, the key design pattern is <strong>SPA + secure token service + Azure Fluid Relay<\/strong>, with secrets stored in <strong>Key Vault<\/strong> and strong user authentication via <strong>Entra ID<\/strong>. Cost and performance hinge on <strong>concurrency<\/strong> and <strong>operation volume<\/strong>, especially presence\/cursor-style updates\u2014throttle and model usage early. Security hinges on keeping tenant keys out of clients and issuing <strong>short-lived, least-privilege tokens<\/strong>.<\/p>\n\n\n\n<p>If your next step is hands-on mastery, take the lab in this tutorial and then evolve it into a production-style design: Entra ID login, token service hosted on Azure Functions, Key Vault secret rotation, and end-to-end monitoring with Application Insights and Azure Monitor.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Web<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[40,42],"tags":[],"class_list":["post-528","post","type-post","status-publish","format-standard","hentry","category-azure","category-web"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/528","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/comments?post=528"}],"version-history":[{"count":0,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/528\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/media?parent=528"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/categories?post=528"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/tags?post=528"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}