Featured resource
2026 Tech Forecast
2026 Tech Forecast

1,500+ tech insiders, business leaders, and Pluralsight Authors share their predictions on what’s shifting fastest and how to stay ahead.

Download the forecast
  • Lab
    • Libraries: If you want this lab, consider one of these libraries.
    • Core Tech
Labs

Guided: Calling Secure Web APIs from Blazor Across Render Modes

Blazor's unified hosting model in .NET 10 lets you mix static SSR, interactive server, and WebAssembly render modes in a single application — but calling a secured backend API correctly from each mode requires very different strategies. A token that's safe in server-side code becomes a leaked secret when your component runs in the browser. In this guided lab, you'll build a product catalog app that calls an authenticated Web API from every render mode without exposing credentials. You'll implement the Backend-for-Frontend (BFF) proxy pattern, configure IHttpClientFactory with named clients, extract a shared IProductService interface so your components work across any render mode, and leverage .NET 10's [SupplyParameterFromPersistentComponentState] attribute to eliminate redundant API calls during prerendering. You'll also diagnose the most common security and lifecycle pitfalls. Walk away with a production-ready playbook for secure API integration in any Blazor application.

Lab platform
Lab Info
Level
Intermediate
Last updated
Apr 29, 2026
Duration
45m

Contact sales

By clicking submit, you agree to our Privacy Policy and Terms of Use, and consent to receive marketing emails from Pluralsight.
Table of Contents
  1. Challenge

    ### Introduction to Secure API Calls Across Blazor Render Modes

    Welcome to this guided lab on calling secure Web APIs from Blazor in .NET 10!

    Blazor's flexible hosting model

    Blazor's unified hosting model lets a single application combine static server-side rendering, interactive server rendering over SignalR, and full WebAssembly interactivity in the browser. This flexibility creates a real security problem.

    Code that looks identical in a .razor file can execute on the server in one component and inside the user's browser in another. A Bearer token that's perfectly safe when injected on the server becomes a leaked credential the moment the same component runs under WebAssembly.

    What you'll build and learn

    In this lab, you'll learn exactly where your API-calling code executes in each render mode and how to structure your service layer so that secrets never cross the server/browser boundary. You'll implement the Backend-for-Frontend (BFF) pattern to shield credentials from client-side code, and you'll write a single IProductService abstraction that works across all render modes without requiring changes to your components.

    You'll also learn to diagnose the two most common real-world pitfalls:

    • Accidentally injecting server-only services into WebAssembly components
    • Double API calls caused by prerendering, which .NET 10's new persistent component state attribute can fix elegantly

    These skills are essential the moment you move past demo apps. Every production Blazor app that talks to authenticated backends needs this mental model, and getting it wrong is how real credential leaks happen.

    Solutions

    If you get stuck at any point, a solutions/ folder is included with the completed code for every step.

    Run the lab

    You can click the Run button to start up both the mock Product API and the UI for the demo application.

    The application can be viewed in the Web Browser at {{localhost:8090}}.

  2. Challenge

    ### Explore the Starter Project and Understand Render Modes

    Before you write any API-calling code, you need a rock-solid mental model of where that code will execute. Blazor's render modes look cosmetically similar in markup—they're all .razor files—but under the hood they run in three completely different environments:

    • Inside the HTTP request pipeline on the server (static SSR),
    • Inside a long-lived SignalR circuit on the server (interactive server),
    • Inside a WebAssembly runtime in the user's browser (WebAssembly).

    Every security decision in this lab hinges on that distinction. In this step you'll enable interactive render modes in the Blazor Web App, add Bearer-token authentication to the backend API so it's actually secured, and author the WebAssembly page you'll integrate with in Step 4. A Blazor Web App has to opt in to each render mode it wants to support, and this opt-in has to happen in two places that both line up. The service registration tells DI to stand up the rendering infrastructure. The endpoint mapping configures the routing layer to connect those rendering pipelines to HTTP responses.

    If either half is missing, @rendermode directives silently fall back to static rendering and your components stop being interactive. This can be a confusing failure mode because there's no runtime error, just pages that mysteriously don't respond to clicks.

    The starter project ships with only static SSR enabled, so you'll add interactive server and WebAssembly support yourself. The goal of this lab is to call a secured Web API. If the API isn't actually enforcing authentication, the security patterns you implement later are theatrical—they don't matter if the upstream accepts anonymous requests.

    In this step, you'll add a minimal Bearer token check to the ProductApi project so that the /api/products endpoint requires a valid token. This gives you ownership of the full request path: you'll know exactly what header the client has to send, which makes the later BFF and credential-isolation work concrete rather than abstract. The @rendermode directive at the top of a Razor component is the single most important line of code for reasoning about security in a Blazor Web App. It tells the runtime where the component will run—and that determines what secrets are safe to touch.

    You're going to build the WebAssembly page you'll use in Step 4 right now, so you control the render mode declaration yourself and understand exactly what it means.

    Remember the three execution contexts:

    • No directive = static SSR. Runs on the server during the HTTP request, returns plain HTML, no persistent connection.
    • @rendermode InteractiveServer = runs on the server over a persistent SignalR circuit. Interactive, but still server-side.
    • @rendermode InteractiveWebAssembly = runs inside the user's browser. Any secret in code that reaches this component is visible to anyone with DevTools.
  3. Challenge

    ### Call the API from Static SSR and Interactive Server Modes

    Now that you understand where components execute, you'll implement your first authenticated API calls from components that execute on the server.

    Server-side code has a major advantage: it can safely hold secrets, because the secret never leaves the server process.

    You'll use IHttpClientFactory to manage HTTP client lifetimes correctly (avoiding the notorious socket-exhaustion and stale-DNS issues that plague naive new HttpClient() usage). You'll attach a Bearer token read from server-side configuration, and render products on the Home page. IHttpClientFactory is the standard way to create HTTP clients in .NET. It manages the underlying HttpMessageHandler lifecycle, pools connections efficiently, and lets you centralize per-client configuration like base addresses and default headers.

    In this step, you'll register a named client called "ProductApi" so that any service asking for this client gets one preconfigured with the correct base URL and Bearer token. Rather than calling HttpClient directly from your Razor components, you'll encapsulate the API logic in a dedicated ProductService class. This separation has three concrete benefits:

    • Components stay focused on presentation
    • API logic is unit-testable in isolation
    • You can swap implementations later when you build the WebAssembly version

    This service-layer pattern is one of the cornerstones of maintainable Blazor applications. Now you'll wire the service into an actual component. The Home page uses static SSR, which means the component renders once on the server during the HTTP request and then returns plain HTML to the browser. This is ideal for content that doesn't need client-side interactivity—and it's the render mode where server-side secrets remain on the server.

  4. Challenge

    ### Call the API Safely from WebAssembly Mode Using the BFF Pattern

    This is the pivot point of the lab. Everything you did in Step 3 relied on one fact: the code ran on the server, so the Bearer token was safe.

    In this step, you'll work with LiveCatalog.razor, a component that runs under InteractiveWebAssembly and therefore executes entirely inside the user's browser. If you naively reuse your ProductService as-is, you'll ship the Bearer token inside your client-side assemblies, where anyone with DevTools can read it.

    The industry-standard solution is the Backend-for-Frontend (BFF) pattern. You build a thin proxy endpoint on your Blazor server that the browser calls instead of calling the upstream API directly. The server adds the secret and forwards the request, keeping the credential where it belongs. The BFF proxy is a single endpoint on your Blazor server that the browser can call without authentication (or with the user's session cookie, in a real app) and which in turn calls the authenticated upstream API with the secret.

    Because this proxy runs server-side, it has safe access to the configuration-backed token.

    There's one wrinkle that bites every Blazor team that mixes WebAssembly components with prerendering: by default, an InteractiveWebAssembly component is prerendered on the server first and then rehydrated in the browser. During prerender the component runs inside the server's DI container, which has ProductService registered, but not ClientProductService.

    If you leave prerender on while injecting ClientProductService, the page will throw InvalidOperationException: There is no registered service of type 'ClientProductService'

    You'll fix this properly in Step 5 by introducing a shared IProductService interface that both sides register.

    For now, the cleanest fix is to disable prerender on this page so the component only ever runs in the browser, where ClientProductService lives. The client project (SecureProductCatalog.Client) is a separate assembly that gets compiled to WebAssembly and shipped to the browser. It needs its own service class, because it needs its own DI container — the server's DI container doesn't exist in the browser.

    This client service calls the BFF proxy at /bff/products rather than the upstream API. Because the browser and the Blazor server share an origin, no cross-origin configuration is needed. Now you'll put everything together on the WebAssembly page you authored back in Task 2.3.

    Because LiveCatalog.razor runs in the browser, you'll inject the client-side service, not the server-side one. The call will go through the BFF proxy you just built.

  5. Challenge

    ### Implement a Shared Service Interface Across Render Modes

    You now have two services: ProductService on the server and ClientProductService in the client. From a component’s perspective, they provide the same functionality. Components don’t need to know whether data comes from the upstream API or through a BFF proxy; they just need a list of products.

    This is the textbook case for dependency inversion: introduce a shared interface that both services implement, and let components depend only on the abstraction. This is the pattern the .NET team explicitly recommends for mixed-render-mode Blazor apps, and once you've done it, you can move components between render modes without changing their code. The interface needs to live somewhere both the server project and the client project can reference. You'll create a small shared class library. This is the standard pattern for code that must be available on both sides of the WebAssembly boundary.

    Note: This task has two distinct phases. Get through Phase 1 first and confirm the new project builds before moving on. With the interface defined, both service classes become implementations of it. The DI containers on both sides will register the interface as the service type, mapping to the appropriate concrete class for each environment.

    From the component's perspective, everything is accessed through IProductService, with no mode-specific code. This is the payoff. Now you'll update your Razor components to inject the interface instead of the concrete classes.

    After this change, components can move between render modes freely. The DI container hands them the right implementation without any code changes required.

    As a bonus, you can re-enable prerendering on LiveCatalog.razor because IProductService resolves on both the server (→ ProductService) and the client (→ ClientProductService).

  6. Challenge

    ### Diagnose Common Issues and Wrap Up

    You've built the happy path. Now you'll deliberately break things in the two most common ways real-world Blazor apps fail, see what the failures look like, and fix them.

    First, you'll experience what happens when a server-only service leaks into a WebAssembly component—the kind of mistake that ships credentials to the browser in production. Second, you'll fix the "double-fetch" problem caused by prerendering, using .NET 10's new [PersistentState] attribute, a feature designed to eliminate this class of bug. One of the easiest mistakes in a mixed-render-mode app is to accidentally register the server's concrete ProductService in the client DI container. If the client project somehow compiled and ran this code, the Bearer token would ship to the browser.

    Let's force this scenario to see how Blazor catches it. When an interactive component is rendered, Blazor prerenders it once on the server for fast initial HTML delivery, then rehydrates it again in its interactive context.

    Since .NET 8, the InteractiveWebAssembly render mode prerenders by default. This means your LiveCatalog page is already calling the API twice: once during server-side prerender, and once when the WebAssembly runtime initializes in the browser.

    This behavior can be wasteful at best and a bug at worst (duplicate POSTs, doubled analytics counters).

    .NET 10's [PersistentState] attribute solves this elegantly by serializing data loaded during prerender into the HTML response and automatically rehydrated on the interactive side. As a result, the second fetch never happens. Your final task is to walk through the completed solution and confirm every piece of the system behaves as designed.

    This kind of deliberate verification is what separates code that works on your machine from code that works in production.

  7. Challenge

    ### Conclusion

    What you accomplished

    Outstanding work! You've built a secure, render-mode-aware Blazor application from the ground up. You now understand where C# executes in static SSR, interactive server, and WebAssembly render modes, and you've seen firsthand why that matters for credential safety.

    You've implemented the Backend-for-Frontend pattern to keep secrets on the server while enabling rich WebAssembly interactivity, extracted a shared IProductService interface so your components are fully decoupled from their render mode, and used .NET 10's [PersistentState] attribute to eliminate redundant API calls during prerendering.

    Why this matters

    These skills form the foundation of every serious Blazor application that talks to authenticated backends. The BFF pattern you built here scales directly to real-world scenarios: swap in cookie-based user authentication on the proxy, add rate limiting, plug in distributed caching, and you have the same architecture Fortune 500 companies deploy in production.

    Next steps

    As next steps, consider extending what you built in this lab:

    • Add cookie-based user authentication in front of your BFF
    • Adding Polly-based retry and circuit-breaker policies to your named HttpClient registrations
    • Extending the shared interface pattern to cover POST, PUT and DELETE operations with server-side authorization checks on the proxy endpoints Each of these topics builds directly on what you've done here.

    Continue learning

    I hope this lab inspires you to dive deeper into Blazor's architectural capabilities. There's a rich catalog of Pluralsight content waiting for you. Happy coding!

About the author

Zach is currently a Senior Software Engineer at VMware where he uses tools such as Python, Docker, Node, and Angular along with various Machine Learning and Data Science techniques/principles. Prior to his current role, Zach worked on submarine software and has a passion for GIS programming along with open-source software.

Real skill practice before real-world application

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Learn by doing

Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.

Follow your guide

All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.

Turn time into mastery

On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.

Get started with Pluralsight