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

Implement Generative AI Workflows in Angular

In this Code Lab, learners build an Angular feature that connects user prompts to a generative AI workflow, streams model output into the interface, and enriches responses with application context. They manage AI interaction state, route model access through a secure server-mediated boundary, and handle structured and non-deterministic AI outputs in a way that supports a clear and resilient user experience. The lab focuses on practical implementation of generative AI workflows in Angular applications.

Lab platform
Lab Info
Level
Advanced
Last updated
May 27, 2026
Duration
1h 1m

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

    Welcome to the Implement Generative AI Workflows in Angular Code Lab. In this hands-on lab, you build the document-assistant feature for a customer-facing Angular application — wiring user prompts to a generative AI gateway, streaming Server-Sent Events into the interface, enriching prompts with profile context behind a secure server-mediated boundary, and defending the UI against the non-deterministic shape of AI responses. When every task is complete, you run the full stack and watch your work pay off in the browser: a live streaming response, a verified Bearer token in the request headers, and profile context flowing securely from your session to the gateway.

    About the tools and concepts A `BehaviorSubject` is an RxJS observable that holds a current value and emits it to every new subscriber. It is the standard Angular pattern for exposing reactive state from a service: components subscribe once, and the template re-renders whenever the service pushes a new state object. The same pattern works in every Angular version that ships RxJS, which makes it a safe default for Angular 16 and earlier where Signals are still in developer preview.

    Server-Sent Events (SSE) are a one-way streaming protocol where the server holds the HTTP connection open and writes events as event: <name>\ndata: <json>\n\n. The browser reads each event as it arrives, which is what lets the UI render generated text token-by-token instead of waiting for the entire completion. Angular's HttpClient waits for the full response body, so streaming clients use fetch with the response's ReadableStream instead.

    A server-mediated boundary sits between the frontend and the model provider. The provider's API key lives only on the gateway, not in the browser, and the frontend authenticates to the gateway with a session token. This is the standard pattern for production generative AI: it keeps the provider credential server-side, lets you rate-limit and audit calls in one place, and gives you a clean spot to merge user-specific context into the prompt.

    An HttpInterceptor runs on every outgoing HttpClient request, which makes it the right place to attach the session token in a single, auditable location instead of scattering header logic across services. A safe interceptor never adds the token to arbitrary URLs — it confirms the request targets a trusted API origin first.

    A type guard is a function whose return type is parameter is SomeType. When the function returns true, TypeScript narrows the parameter to that type for the rest of the calling scope, which lets the caller pass an unknown value through a runtime shape check before treating it as the typed response.

    ### Prerequisites

    Before starting this lab, you should be comfortable with:

    • Angular basics — components, templates, and routing
    • TypeScript basics — classes, interfaces, and imports
    • HTTP and async basics — HttpClient, APIs, and asynchronous workflows
    • Reactive state basics — Observables and UI state updates
    • Generative AI basics — prompts, responses, and API-based model interaction The lab environment is ready to use. The AI gateway is already running on port 3000 in the background, and the Angular dev-server proxy forwards /api/* calls from the app to the gateway. Run ng build from inside the AIWorkflowApp folder at any time to verify your changes compile.

    The Scenario

    You are a frontend developer at ContentAI Solutions building a document assistant. The feature has to send user prompts to the AI gateway, render Server-Sent Events as they arrive, enrich prompts with profile context drawn from the authenticated session, and stay resilient when the gateway returns a malformed structured response. Your job is to wire the prompt-and-response workflow, manage the AI interaction state through a BehaviorSubject, route gateway access through an HttpInterceptor that attaches the session token only to allowlisted origins, and validate structured AI output against a runtime type guard before it reaches the UI.

    The Application Structure

    Key files in the lab environment - `AIWorkflowApp/src/app/services/auth.service.ts` — an authentication service that exposes the session token and the current user's profile preferences - `AIWorkflowApp/src/app/services/ai.service.ts` — the AI workflow service that needs to build requests, drive the streaming lifecycle, validate structured responses, and expose state through a `BehaviorSubject` - `AIWorkflowApp/src/app/interceptors/auth.interceptor.ts` — an HTTP interceptor that needs to attach the session token to allowlisted API calls - `AIWorkflowApp/src/app/components/prompt-panel/` — the input panel that submits prompts and toggles profile-derived context - `AIWorkflowApp/src/app/components/response-display/` — the response panel that subscribes to AI state and renders each lifecycle phase - `AIWorkflowApp/src/app/components/assistant-page/` — the page that composes the prompt panel and the response display - `api-server/server.js` — the AI gateway that holds the provider credentials, validates the session token, and streams Server-Sent Events back to the client (already running, no changes needed) Complete the tasks in order. Each task builds on the previous one.
    Run the build at any point with:
    cd AIWorkflowApp && ng build
    

    The first time you run ng build, the CLI asks whether you want to enable autocompletion. Type N and press Enter.

  2. Challenge

    Wiring the Prompt-and-Response Workflow

    Building the Outgoing Request

    Before any UI streaming logic runs, the service has to assemble a clean request body. User input arriving from a textarea routinely carries leading and trailing whitespace, and the gateway treats whitespace-only prompts as billable but useless calls. Trimming the prompt before it enters the request payload is the cheapest place to enforce that rule, and it keeps every downstream component working with the same canonical value.

    The buildRequest method also decides whether profile-derived context travels with the prompt. The first task focuses only on the prompt itself — context-merging comes later, once the secure boundary that protects profile reads is in place.

    Connecting the Component to the Service

    A prompt panel that lets the user click Send while the textarea is empty wastes both a network round-trip and the user's attention. The component class exposes a canSubmit getter the template binds to the button's disabled attribute, which keeps the submission rule in one readable place and the template free of inline expressions.

    The same submit method that the button click triggers also delegates straight to AIService.sendPrompt — the component never owns the lifecycle, it just hands the prompt and the context flag to the service and lets the response display subscribe to the resulting state stream.

  3. Challenge

    Managing AI Interaction State

    Driving the Lifecycle Through a BehaviorSubject

    Generative AI calls do not follow a binary loading-then-loaded model — they pass through a real lifecycle: a brief loading phase while the request is in flight, a streaming phase as Server-Sent Events arrive, and a complete phase once the gateway emits the done event. Each phase needs a different UI treatment, and the cheapest way to drive that from a service is to push a discriminated AIState object into a BehaviorSubject and let every subscriber re-render off the same source of truth.

    The state object carries a status literal, the accumulated text, and an errorMessage slot used in the next task. Each setState call replaces the whole object, which keeps every transition explicit at the call site.

    Surfacing Errors Without Losing Partial Output

    A network failure or a gateway timeout has to surface as a real UI state, not a silent dropped subscription. The .catch block on the fetch promise is the right place to translate a thrown error into a state transition — the service pushes a state with status 'error', preserves whatever text accumulated before the failure, and sets a human-readable errorMessage the response display can render.

    Preserving the in-flight text matters because partial output is often still useful — a half-streamed summary tells the user what the model managed to produce before the connection dropped, which is a better experience than blanking the panel.

  4. Challenge

    Securing the Server-Mediated Boundary

    Centralizing the Session Token in an Interceptor

    The AI gateway sits behind a session-token check, and every HttpClient request from the application has to carry an Authorization: Bearer <token> header. Adding that header inside services that use HttpClient would scatter the rule across every method that calls the gateway, so the application centralizes it in an HttpInterceptor instead — one function the framework runs on every outgoing HttpClient request.

    The interceptor enforces two boundaries before it touches the request. The first is an origin allowlist: the token only attaches to URLs that match a trusted API prefix, so an accidental call to an external host never leaks the credential. The second is a session check: when no token is present, the request goes through unchanged so anonymous calls to public endpoints still work. Both checks live in one place, which makes the rule auditable.

    Folding Profile Context Into the Prompt

    Now that the session token rides every trusted request automatically, the AI service can safely read profile preferences and fold them into the request body. The includeContext flag from the prompt panel decides whether profile-derived context travels with the prompt — when the flag is on, the service reads tone, language, and expertiseLevel from the user profile and packs them into the request; when the flag is off, the context field is null and the gateway treats the call as a context-free prompt.

    Keeping the profile read inside AIService rather than inside the component class means the trust boundary stays in one auditable place — the component never sees the token and never reads the profile directly.

  5. Challenge

    Handling Non-Deterministic AI Outputs

    Treating the Gateway Body as Untrusted

    A streaming text completion has a forgiving shape — any string is a valid response, and the UI can simply render whatever arrives. A structured completion is the opposite: the gateway is supposed to return JSON shaped like the StructuredAIResponse interface, but a non-deterministic model occasionally returns a missing field, an extra field with the right name but a wrong type, or a response that parses as valid JSON but lacks the expected structure entirely.

    The fetchStructured method types the parsed body as unknown and routes it through a runtime type guard before treating it as the expected shape. When the guard rejects the payload, the method throws an error, and the observable's error path runs — downstream callers get a typed failure they can retry, log, or surface to the user, instead of a runtime crash deeper in the render pipeline.

    Building the Type Guard Itself

    The type guard from the previous task is only as strict as the field checks inside it. A full guard inspects every required field — the model occasionally omits one, swaps a string for a number, or returns a single string where the interface expects an array — so each field gets its own boolean check, and the guard returns true only when every check passes.

    Each check uses the narrowest possible test for the field's type. typeof covers primitive shapes like string and number, and Array.isArray is the only correct way to check for an array — typeof [] === 'object', so typeof would not catch the case where the model returns an object instead of an array.

  6. Challenge

    View your Application

    View the Application

    Now that every task is complete, run the application and view it in the browser to see the full workflow.

    1. Start the app from the AIWorkflowApp directory with ng serve --host 0.0.0.0 --port 4200. The dev-server already loads proxy.conf.json, which forwards /api/* calls to the gateway running on port 3000.

    2. Open the Web Browser panel on the right, navigate to localhost:4200 and click the external window icon in the top-right corner of the panel to open the app in a new browser tab. Before typing anything, open DevTools (F12) and go to the Network tab.

      Note: Requests that arrive before DevTools is open do not appear in the panel.

    3. In the application, type a prompt into the textarea (for example, Tell me something about football) and confirm the Send button stays disabled until you have non-whitespace text.

    4. Click Send and watch the response display badge cycle from loading to streaming to complete as the SSE chunks arrive.

    5. In the Network tab, select the stream request under the Fetch/XHR filter and click the Payload tab. Confirm the payload carries your prompt and that context contains tone, language, and expertiseLevel from your profile.

    6. Click Clear, uncheck Use my profile preferences, type the same prompt again, and click Send. Select the new stream request, open the Payload tab, and confirm context is now null.

    7. With that request still selected, click the Headers tab. Under Request Headers, confirm authorization: Bearer demo-session-token-abc123 is present. Under Response Headers, confirm content-type: text/event-stream.

    Expected Result: Every part of the workflow you implemented is visible in the running application. The prompt panel forwards input only when there's real content, the response display reacts to each lifecycle phase, the interceptor attaches the session token to allowlisted calls, and the structured-response path rejects malformed payloads before they reach the UI.

  7. Challenge

    Conclusion

    Congratulations on completing the Implement Generative AI Workflows in Angular lab! You have wired a Server-Sent Events streaming workflow, driven the AI interaction lifecycle through a BehaviorSubject, centralized session-token handling in an HttpInterceptor, and validated structured AI output against a runtime type guard.

    What You Have Accomplished

    1. Trimmed the Outgoing Prompt — Cleaned up user input inside buildRequest so the gateway never sees a whitespace-only prompt.
    2. Wired the Prompt Panel to the Service — Bound the Send button to a canSubmit getter and forwarded the prompt to AIService.sendPrompt from the component.
    3. Drove the Streaming Lifecycle — Pushed loading, streaming, and complete states into the BehaviorSubject so the response display re-renders off a single source of truth.
    4. Captured Failed Requests in the Error State — Translated thrown errors into a state transition that preserves any partial output already on screen.
    5. Attached the Session Token to Allowlisted Requests — Centralized the Authorization header in an HttpInterceptor that only touches trusted API origins.
    6. Enriched Trusted Requests with Profile Context — Folded the user's tone, language, and expertise level into the request body when the caller opts in.
    7. Validated Structured Responses Through a Type Guard — Routed every parsed body through a runtime check before treating it as the typed AI response.
    8. Implemented Per-Field Shape Checks — Wrote the type guard's field-by-field checks so missing, mistyped, or wrongly-shaped payloads fail loudly instead of corrupting the UI.
    9. Saw the Full Workflow in Action — Ran both servers, watched the badge cycle from loading to streaming to complete in real time, and verified the request payload, Bearer token, and text/event-stream content type in DevTools — confirming every piece you built is working end-to-end.

    Key Takeaways

    • A BehaviorSubject per service is the simplest way to drive a multi-phase reactive lifecycle in Angular 16 — every component subscribes once and re-renders off the same state object.
    • Server-Sent Events are the natural fit for token-by-token streaming from an AI gateway; reading the response with fetch and a ReadableStream lets the UI update as each event arrives instead of waiting for the entire completion.
    • An HttpInterceptor turns a per-call concern into a single, auditable rule — and limiting that rule to a trusted-origin allowlist prevents credential leaks to external hosts.
    • The trust boundary for profile-derived context belongs in the service, not the component — the component never needs to see the token or the profile to send an enriched prompt.
    • Generative AI responses are non-deterministic by design; treating every parsed body as unknown until a runtime type guard validates it is the cheapest defence against shape drift.

    Experiment Before You Go

    You still have time in the lab environment. Try these explorations:

    • Add a fourth lifecycle status 'cancelled' and a cancel() method on AIService that calls the stored streamReader.cancel() and pushes a cancelled state into the subject
    • Extend the trusted-origin allowlist with a second prefix and confirm the interceptor attaches the token to both
    • Add a retry-on-malformed-output branch around fetchStructured that calls the endpoint again with a stricter system prompt when the type guard fails
    • Replace the BehaviorSubject with an Angular Signal (Angular 17+) and observe how the same lifecycle reads in the template
About the author

Angel Sayani is a Certified Artificial Intelligence Expert®, CEO of IntellChromatics, author of two books in cybersecurity and IT certifications, world record holder, and a well-known cybersecurity and digital forensics expert.

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