<?xml version="1.0" encoding="UTF-8" ?>
		<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:webfeeds="http://webfeeds.org/rss/1.0" version="2.0">
		<channel>
		<title><![CDATA[ Tim Deschryver ]]></title>
		<description><![CDATA[ Blog by Tim Deschryver ]]></description>
		<lastBuildDate>Wed, 29 Jul 2026 00:00:00 GMT</lastBuildDate>
		<ttl>60</ttl>
		<language>en-us</language>
		<link>https://timdeschryver.dev/blog</link>
		<atom:link href="https://timdeschryver.dev/blog/rss.xml" rel="self" type="application/rss+xml"/>
		<image>
	<url>https://timdeschryver.dev/favicons/favicon-32x32.png</url>
	<title>Tim Deschryver</title>
	<link>https://timdeschryver.dev/blog</link>
	</image>
		
				<item>
					<title><![CDATA[Testing AI prompts and comparing models with promptfoo]]></title>
					<description><![CDATA[Just like other software components, prompts benefit from tests that provide representative inputs to verify that the outputs meet the requirements. With promptfoo, you can test prompt behavior, compare models, and track output quality and keep runtime costs in check.]]></description>
					<link>https://timdeschryver.dev/blog/testing-ai-prompts-and-comparing-models-with-promptfoo</link>
					<guid>https://timdeschryver.dev/blog/testing-ai-prompts-and-comparing-models-with-promptfoo</guid>
					<pubDate>Wed, 29 Jul 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/testing-ai-prompts-and-comparing-models-with-promptfoo/images/banner.webp" alt="Testing AI prompts and comparing models with promptfoo"/><p>Read <strong>Testing AI prompts and comparing models with promptfoo</strong> on <a href="https://timdeschryver.dev/blog/testing-ai-prompts-and-comparing-models-with-promptfoo">timdeschryver.dev</a></p><p>As AI becomes more integrated into our daily workflows, we start to see that applications also increasingly rely on prompts to make certain tasks easier or more efficient.</p><p>However, prompts may not always have the expected outcome.
Even the most basic prompts can produce unexpected results, and small changes can introduce regressions that are hard to detect without systematic testing.
Not only changes to the prompt itself, but also changes to the model, or changing the model itself, can affect the output quality and cost.</p><p>Just like other software components, prompts benefit from tests that provide representative inputs to verify that the outputs meet the requirements.</p>
		<h2 id="why-automate-prompt-testing">
		  <a href="#why-automate-prompt-testing" class="anchor mark-hover" tabindex="-1">Why automate prompt testing</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>When an application uses only one prompt, it&#39;s manageable to test it manually and optimize it until the prompt produces the expected output for various scenarios.
However, this process should be repeated when the prompt changes or when a different model is used.
As the number of prompts and criteria grows, manual verification quickly becomes time-consuming, and difficult to repeat consistently within a team.</p><p><a class="mark mark-hover" href="https://promptfoo.dev/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fpromptfoo.dev)'>Promptfoo</a> automates this process by running a set of prompts and inputs against an AI model.
It evaluates the responses using predefined assertions, making it easier to repeat the same checks after every change.
This ensures that the prompt continues to meet the requirements and that the output quality is not compromised due to different factors.</p>
		<h3 id="compare-models-without-sacrificing-quality">
		  <a href="#compare-models-without-sacrificing-quality" class="anchor mark-hover" tabindex="-1">Compare models without sacrificing quality</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Next to testing prompt behavior, promptfoo makes it easier to compare and evaluate multiple models.
Automated evaluations help catch regressions, and it also helps when the application switches to another model.
This is important because models can behave differently and may require different prompts to achieve comparable results.</p><p>Even the smallest subtle change in a prompt can have a significant impact on the output quality, and it can be difficult to determine whether a different model is suitable without running the same tests against it.</p><p>Running the same test cases against multiple models lets you compare output quality, token usage, and cost.
This is critical to determine whether a cheaper model meets the same requirements, and to switch models without unknowingly sacrificing quality.</p><p>Promptfoo also supports multiple providers, so you can compare models from different vendors within the same evaluation.</p>
		<h2 id="what-can-you-test">
		  <a href="#what-can-you-test" class="anchor mark-hover" tabindex="-1">What can you test?</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>With promptfoo, you can define prompts and create test cases for the given prompts.
Each test case provides assertions that describe the expected output, and can include variables to add dynamic content to the prompt.
Promptfoo renders the prompt, sends it to every configured model, and evaluates each response.</p><p>The assertions broadly fall into two categories: deterministic assertions and model-graded assertions.</p>
		<h3 id="deterministic-assertions">
		  <a href="#deterministic-assertions" class="anchor mark-hover" tabindex="-1">Deterministic assertions</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Deterministic assertions evaluate a response using explicit rules.
Given the same response and assertion, they always produce the same result.
They are useful when the requirement can be expressed precisely, for example:</p><ul>
<li>Comparing the output with an expected value using <code>equals</code>, <code>contains</code>, <code>not-contains</code>, or a regular expression.</li>
<li>Verifying the response length with <code>word-count</code>.</li>
<li>Validating structured output, such as JSON that must match a JSON schema.</li>
<li>Comparing semantic similarity using vector embeddings.</li>
</ul>
<p>When the multiple built-in assertions are not sufficient, you can also implement a custom rule with JavaScript, Python, or Ruby.
E.g. for complext JSON schemas, you can write a zod schema validator.</p><p>Not only can you check the correctness of the output, but you can also verify that it meets performance requirements with assertions to check to cost, latency, and token usage.</p><p>These assertions are usually the best starting point because their results are predictable and easy to debug.</p><p>See the <a class="mark mark-hover" href="https://www.promptfoo.dev/docs/configuration/expected-outputs/deterministic/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.promptfoo.dev)'>Deterministic metrics</a> documentation for the complete list.</p>
		<h3 id="model-graded-assertions">
		  <a href="#model-graded-assertions" class="anchor mark-hover" tabindex="-1">Model-graded assertions</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Some requirements are difficult to describe with an exact rule.
Model-graded assertions use another language model as a judge to evaluate more subjective qualities, such as:</p><ul>
<li>Whether the response uses the expected tone or writing style.</li>
<li>Whether an answer is relevant, complete, and appropriate for its audience.</li>
<li>Whether a summary preserves the important information.</li>
<li>Whether the response follows a natural-language rubric or remains factually consistent with a reference.</li>
</ul>
<p>Use them for requirements that genuinely need judgment, and prefer deterministic assertions wherever an explicit rule is sufficient.</p><p>A thing to watch out for, because model-graded assertions require an additional model call and can be less predictable than deterministic checks, they also add latency and cost.</p><p>See the <a class="mark mark-hover" href="https://www.promptfoo.dev/docs/configuration/expected-outputs/model-graded/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.promptfoo.dev)'>Model-graded metrics</a> documentation for the available graders.</p>
		<h2 id="a-practical-promptfoo-example">
		  <a href="#a-practical-promptfoo-example" class="anchor mark-hover" tabindex="-1">A practical promptfoo example</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Promptfoo evaluations are defined in a configuration file containing the models, prompt, test inputs, and assertions.
This makes the evaluation repeatable and ensures that every model receives the same inputs and is measured against the same expectations.</p><p>In this example, <code>promptfooconfig.yaml</code> tests a practical data-extraction task: extracting information from fictional customer messages and returning it as structured JSON.
This scenario is useful because the output must satisfy both strict requirements, such as valid JSON, and qualitative requirements, such as not inventing information that is missing from the message.</p><p>The configuration compares <a class="mark mark-hover" href="https://developers.openai.com/api/docs/models/gpt-5.6-luna" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fdevelopers.openai.com)'>GPT-5.6 Luna</a>, the efficient tier, with <a class="mark mark-hover" href="https://developers.openai.com/api/docs/models/gpt-5.6-sol" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fdevelopers.openai.com)'>GPT-5.6 Sol</a>, the flagship tier.</p><p>The test cases use a mix of a deterministic and model-graded assertions to verify that the output is valid JSON, contains the expected keys, and does not invent values that are not present in the source message.</p><p>Instead of defining the configuration in YAML, you can also define it in TypeScript, as shown as well in the example below.
The TypeScript version produces the same test cases programmatically, which can be useful when test data needs to be transformed or generated.</p><p>In the prompt, variables can be inserted using the <a class="mark mark-hover" href="https://www.promptfoo.dev/docs/configuration/guide/#using-nunjucks-templates" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.promptfoo.dev)'>Nunjucks template syntax</a>, for example, a placeholder such as <code>{{text}}</code> is replaced with the matching value from <code>vars</code> before the prompt is sent to a model.</p>
        <div class="code-group">
			<div class="code-group-tabs" role="tablist" aria-label="Code examples"> <button type="button" id="code-tab-171" data-id="171" class="code-group-tab active" role="tab" aria-selected="true" aria-controls="code-panel-171" tabindex="0">YAML</button><button type="button" id="code-tab-172" data-id="172" class="code-group-tab " role="tab" aria-selected="false" aria-controls="code-panel-172" tabindex="-1">TypeScript</button></div>
			<div id="code-panel-171" data-id="171" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-171" ><pre id="4ab74c0e37655809aede14bbb6dced35"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.53267 5.86364H9.28835L11.9418 10.875H12.0554L14.7088 5.86364H17.4645L13.2202 13.3864V17.5H10.777V13.3864L6.53267 5.86364Z" fill="#A78BFA"/>
</svg>
 <span class="file-name">promptfooconfig.yaml</span> <button type="button" class="copy-code icon align-text-top" data-ref="4ab74c0e37655809aede14bbb6dced35" data-status="4ab74c0e37655809aede14bbb6dced35-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="4ab74c0e37655809aede14bbb6dced35-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="yaml " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">prompts</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">|</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">    Extract the person's name and email address from the following message.</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">    Respond with a JSON object containing only the keys &quot;name&quot; and &quot;email&quot;.</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">    Do not infer values that are not explicitly present.</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">    Use an empty string when a value is not present.</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">    {{text}}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">providers</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">openai:responses:gpt-5.6-luna</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">openai:responses:gpt-5.6-sol</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">defaultTest</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-entity-name-section))">assert</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">is-json</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">object</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">required</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">name</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">email</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">additionalProperties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">string</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">string</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">llm-rubric</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">|</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">        Compare the response with the following source message:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">        {{text}}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">        Pass only when every non-empty name and email address in the</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">        response is explicitly present in the source message. The response</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">        must not invent values.</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">cost</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">threshold</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">0.005</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">tests</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">vars</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">text</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Hi, my name is Alex Johnson. Contact me at alex.johnson@example.com.'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-entity-name-section))">assert</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">is-json</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Alex Johnson'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'alex.johnson@example.com'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">vars</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">text</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Please send the invoice to Priya Shah at priya.shah@example.org.'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-entity-name-section))">assert</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">is-json</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Priya Shah'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'priya.shah@example.org'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">vars</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">text</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'I cannot access my account after resetting the password.'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-entity-name-section))">assert</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">is-json</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span></div></code></pre></div><div id="code-panel-172" data-id="172" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-172" hidden><pre id="aa8151cc0e721acf5c082dc85a812890"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">promptfooconfig.ts</span> <button type="button" class="copy-code icon align-text-top" data-ref="aa8151cc0e721acf5c082dc85a812890" data-status="aa8151cc0e721acf5c082dc85a812890-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="aa8151cc0e721acf5c082dc85a812890-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import type </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">UnifiedConfig</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'promptfoo'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">cases</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Alex Johnson'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'alex.johnson@example.com'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    text</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Hi, my name is Alex Johnson. Contact me at alex.johnson@example.com.'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Priya Shah'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'priya.shah@example.org'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    text</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Please send the invoice to Priya Shah at priya.shah@example.org.'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    text</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'I cannot access my account after resetting the password.'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">config</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">UnifiedConfig</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  prompts</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-property-value-css))">`Extract the person's name and email address from the following message.</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">Respond with a JSON object containing only the keys &quot;name&quot; and &quot;email&quot;.</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">Do not infer values that are not explicitly present.</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">Use an empty string when a value is not present.</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">{{text}}`</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  ]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  providers</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'openai:responses:gpt-5.6-luna'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'openai:responses:gpt-5.6-sol'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  ]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  defaultTest</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    assert</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'is-json'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'object'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          required</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'name'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'email'</span><span style="color: hsl(var(--syntax-meta-tag))">]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          additionalProperties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'string'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'string'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'llm-rubric'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">`Compare the response with the following source message:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">{{text}}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">Pass only when every non-empty name and email address in the response is</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">explicitly present in the source message. The response must not invent values.`</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'cost'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        threshold</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">0.005</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    ]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  tests</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">cases</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">map</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">({</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">text</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> (</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    vars</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-meta-tag))">text</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    assert</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'is-json'</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">as</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    ]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">))</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export default </span><span style="color: hsl(var(--syntax-meta-tag))">config</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre></div>
		</div><p>In the example above, the shared assertions are defined once under <code>defaultTest</code>, while each test case provides a different text and a content-specific assertion.</p><p>Each text is evaluated against both models using the inherited and test-specific assertions, making it easy to compare output quality and cost across a broader set of inputs.</p><p>You can also notice that Nunjucks variables can also be used in assertion values.
In this exmaple, the <code>llm-rubric</code> assertion uses <code>{{text}}</code> to provide the grading model with the source message.</p>
		<h2 id="load-prompts-and-variables-from-files">
		  <a href="#load-prompts-and-variables-from-files" class="anchor mark-hover" tabindex="-1">Load prompts and variables from files</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>As the test suite grows, keeping prompts and test data in separate files makes them easier to maintain and reuse.
The customer-message example from the previous section can be refactored into the following feature-based structure:</p><pre id="e6bf2ed0cb09a8b986c7a14571b3607c"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="e6bf2ed0cb09a8b986c7a14571b3607c" data-status="e6bf2ed0cb09a8b986c7a14571b3607c-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="e6bf2ed0cb09a8b986c7a14571b3607c-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="text " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-unknown))">promptfoo</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">├── customer-message</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   ├── fixtures</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   │   └── customer-message.txt</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   ├── prompts</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   │   └── extract-personal-information-v1.txt</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   ├── tests</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   │   └── extract-personal-information.yaml</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   └── promptfooconfig.yaml</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">└── providers.yaml</span></div></code></pre><p>The feature&#39;s <code>promptfooconfig.yaml</code> references its prompt and test files using the <code>file://</code> prefix, as well as the shared provider configuration one directory above it:</p><pre id="76a208bb381b2c030148067aabca7416"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.53267 5.86364H9.28835L11.9418 10.875H12.0554L14.7088 5.86364H17.4645L13.2202 13.3864V17.5H10.777V13.3864L6.53267 5.86364Z" fill="#A78BFA"/>
</svg>
 <span class="file-name">[title=customer-message/promptfooconfig.yaml]</span> <button type="button" class="copy-code icon align-text-top" data-ref="76a208bb381b2c030148067aabca7416" data-status="76a208bb381b2c030148067aabca7416-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="76a208bb381b2c030148067aabca7416-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="yaml " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">prompts</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">file://prompts/extract-personal-information-v1.txt</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">providers</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">file://../providers.yaml</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">defaultTest</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-entity-name-section))">assert</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">is-json</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">object</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">required</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">name</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">email</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">additionalProperties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">string</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">string</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">llm-rubric</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">|</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">        Compare the response with the following source message:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">        {{text}}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">        Pass only when every non-empty name and email address in the</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">        response is explicitly present in the source message. The response</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">        must not invent values.</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">cost</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">threshold</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">0.005</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">tests</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">file://tests/extract-personal-information.yaml</span></div></code></pre><p>The contents of these files are identical to the inline example, except that the prompt and test cases are now stored in separate files.</p>
        <div class="code-group">
			<div class="code-group-tabs" role="tablist" aria-label="Code examples"> <button type="button" id="code-tab-173" data-id="173" class="code-group-tab active" role="tab" aria-selected="true" aria-controls="code-panel-173" tabindex="0">providers.yaml</button><button type="button" id="code-tab-174" data-id="174" class="code-group-tab " role="tab" aria-selected="false" aria-controls="code-panel-174" tabindex="-1">extract-personal-information-v1.txt</button><button type="button" id="code-tab-175" data-id="175" class="code-group-tab " role="tab" aria-selected="false" aria-controls="code-panel-175" tabindex="-1">extract-personal-information.yaml</button><button type="button" id="code-tab-176" data-id="176" class="code-group-tab " role="tab" aria-selected="false" aria-controls="code-panel-176" tabindex="-1">customer-message.txt</button></div>
			<div id="code-panel-173" data-id="173" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-173" ><pre id="416ad8fcafc253c02e3f0874cc1f520d"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.53267 5.86364H9.28835L11.9418 10.875H12.0554L14.7088 5.86364H17.4645L13.2202 13.3864V17.5H10.777V13.3864L6.53267 5.86364Z" fill="#A78BFA"/>
</svg>
 <span class="file-name">providers.yaml</span> <button type="button" class="copy-code icon align-text-top" data-ref="416ad8fcafc253c02e3f0874cc1f520d" data-status="416ad8fcafc253c02e3f0874cc1f520d-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="416ad8fcafc253c02e3f0874cc1f520d-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="yaml " tabindex="0"><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">openai:responses:gpt-5.6-luna</span></div><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">openai:responses:gpt-5.6-sol</span></div></code></pre></div><div id="code-panel-174" data-id="174" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-174" hidden><pre id="41d64195f545f07cba3c0d23c55d7143"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-message/prompts/extract-personal-information-v1.txt</span> <button type="button" class="copy-code icon align-text-top" data-ref="41d64195f545f07cba3c0d23c55d7143" data-status="41d64195f545f07cba3c0d23c55d7143-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="41d64195f545f07cba3c0d23c55d7143-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="text " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-unknown))">Extract the person's name and email address from the following message.</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">Respond with a JSON object containing only the keys &quot;name&quot; and &quot;email&quot;.</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">Do not infer values that are not explicitly present.</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">Use an empty string when a value is not present.</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))"></span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">{{text}}</span></div></code></pre></div><div id="code-panel-175" data-id="175" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-175" hidden><pre id="610c36f390848e2020b193d825eef52b"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.53267 5.86364H9.28835L11.9418 10.875H12.0554L14.7088 5.86364H17.4645L13.2202 13.3864V17.5H10.777V13.3864L6.53267 5.86364Z" fill="#A78BFA"/>
</svg>
 <span class="file-name">customer-message/tests/extract-personal-information.yaml</span> <button type="button" class="copy-code icon align-text-top" data-ref="610c36f390848e2020b193d825eef52b" data-status="610c36f390848e2020b193d825eef52b-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="610c36f390848e2020b193d825eef52b-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="yaml " tabindex="0"><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">vars</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-entity-name-section))">text</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">file://fixtures/customer-message.txt</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-entity-name-section))">assert</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">is-json</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Alex Johnson'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'alex.johnson@example.com'</span></div><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">vars</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-entity-name-section))">text</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Please send the invoice to Priya Shah at priya.shah@example.org.'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-entity-name-section))">assert</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">is-json</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Priya Shah'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'priya.shah@example.org'</span></div><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">vars</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-entity-name-section))">text</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'I cannot access my account after resetting the password.'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-entity-name-section))">assert</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">is-json</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-name-section))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-entity-name-section))">email</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-name-section))">const</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span></div></code></pre></div><div id="code-panel-176" data-id="176" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-176" hidden><pre id="518642ce21a24e82fc6d8791a3008168"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-message/fixtures/customer-message.txt</span> <button type="button" class="copy-code icon align-text-top" data-ref="518642ce21a24e82fc6d8791a3008168" data-status="518642ce21a24e82fc6d8791a3008168-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="518642ce21a24e82fc6d8791a3008168-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="text " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-unknown))">Hi, my name is Alex Johnson. Contact me at alex.johnson@example.com.</span></div></code></pre></div>
		</div><p>In this example, promptfoo loads the customer message from the fixture instead of embedding it in the configuration. All <code>file://</code> paths are resolved relative to <code>promptfooconfig.yaml</code>, regardless of the directory from which the command is run.</p>
		<h3 id="compare-multiple-prompts-for-the-same-task">
		  <a href="#compare-multiple-prompts-for-the-same-task" class="anchor mark-hover" tabindex="-1">Compare multiple prompts for the same task</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>When two prompts solve the same task, add both prompt files to the same configuration:</p><pre id="34c07fab1f716bfa279d1b1e18fc7963"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.53267 5.86364H9.28835L11.9418 10.875H12.0554L14.7088 5.86364H17.4645L13.2202 13.3864V17.5H10.777V13.3864L6.53267 5.86364Z" fill="#A78BFA"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="34c07fab1f716bfa279d1b1e18fc7963" data-status="34c07fab1f716bfa279d1b1e18fc7963-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="34c07fab1f716bfa279d1b1e18fc7963-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="yaml " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">prompts</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">file://prompts/extract-personal-information-v1.txt</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">file://prompts/extract-personal-information-v2.txt</span></div></code></pre><p>By default, promptfoo runs every test against every prompt and provider.
This is useful for comparing prompt variants because each combination receives the same inputs and assertions.</p>
		<h3 id="organize-different-tasks-by-feature">
		  <a href="#organize-different-tasks-by-feature" class="anchor mark-hover" tabindex="-1">Organize different tasks by feature</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>As an application grows, it will probably contain different types of prompts for unrelated tasks with different variables and expectations.
In that case, I prefer to use a separate configuration for each task instead of placing every prompt and test in one configuration.</p><p>A larger test suite could use the following structure:</p><pre id="ea35e2d96a34664583157f203c07b7d5"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="ea35e2d96a34664583157f203c07b7d5" data-status="ea35e2d96a34664583157f203c07b7d5-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="ea35e2d96a34664583157f203c07b7d5-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="text " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-unknown))">promptfoo</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">├── customer-message</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   ├── fixtures</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   │   └── customer-message.txt</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   ├── prompts</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   │   ├── extract-personal-information-v1.txt</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   │   └── extract-personal-information-v2.txt</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   ├── tests</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   │   └── extract-personal-information.yaml</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   └── promptfooconfig.yaml</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">├── billing-request</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   ├── fixtures</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   │   └── billing-request.txt</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   ├── prompts</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   │   └── classify-support-request.txt</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   ├── tests</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   │   └── classify-support-request.yaml</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">│   └── promptfooconfig.yaml</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">└── providers.yaml</span></div></code></pre><p>Each feature directory now contains its configuration, prompts, fixtures, and tests.
This keeps files that change together close to each other while the model configuration remains shared.
For example, <code>customer-message/promptfooconfig.yaml</code> can contain:</p><pre id="8894855f969f34aa378ccd0ecbe9d9b5"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.53267 5.86364H9.28835L11.9418 10.875H12.0554L14.7088 5.86364H17.4645L13.2202 13.3864V17.5H10.777V13.3864L6.53267 5.86364Z" fill="#A78BFA"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="8894855f969f34aa378ccd0ecbe9d9b5" data-status="8894855f969f34aa378ccd0ecbe9d9b5-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="8894855f969f34aa378ccd0ecbe9d9b5-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="yaml " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">description</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Extract personal information from customer messages'</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">prompts</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">file://prompts/extract-personal-information-v1.txt</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">file://prompts/extract-personal-information-v2.txt</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">providers</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">file://../providers.yaml</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">tests</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">file://tests/extract-personal-information.yaml</span></div></code></pre><p>Both feature configurations can reference the root-level <code>providers.yaml</code> shown earlier, ensuring that every task evaluates the same models.</p><p>Run one task while working on a specific prompt (more on this in the next section):</p><pre id="42ac31c0c2099dd5de9872d88a0344e1"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="42ac31c0c2099dd5de9872d88a0344e1" data-status="42ac31c0c2099dd5de9872d88a0344e1-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="42ac31c0c2099dd5de9872d88a0344e1-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">pnpm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">exec</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">promptfoo</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">eval</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">--config</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">promptfoo/customer-message/promptfooconfig.yaml</span></div></code></pre><p>Or run every configuration to evaluate the complete prompt suite:</p><pre id="d366fe708ec9c81816cb7779da4e18b3"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="d366fe708ec9c81816cb7779da4e18b3" data-status="d366fe708ec9c81816cb7779da4e18b3-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="d366fe708ec9c81816cb7779da4e18b3-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">pnpm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">exec</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">promptfoo</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">eval</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">--config</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">promptfoo/</span><span style="color: hsl(var(--syntax-meta-tag))">*</span><span style="color: hsl(var(--syntax-meta-property-value-css))">/promptfooconfig.yaml</span></div></code></pre><p>This structure keeps prompts, inputs, and assertions close to their use case while still making model selection reusable across the entire suite.</p>
		<h2 id="set-up-promptfoo">
		  <a href="#set-up-promptfoo" class="anchor mark-hover" tabindex="-1">Set up promptfoo</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>If you&#39;re hooked and want to try promptfoo yourself, you can follow the steps below to set up the example from this post.
Promptfoo can be installed as a development dependency. The examples below use pnpm:</p><pre id="5c9f941096e021142c6f7dafe4428495"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="5c9f941096e021142c6f7dafe4428495" data-status="5c9f941096e021142c6f7dafe4428495-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="5c9f941096e021142c6f7dafe4428495-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">pnpm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">--save-dev</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">promptfoo</span></div></code></pre><p>You can then use the interactive setup to create an example <code>promptfooconfig.yaml</code> file with minimal effort.
In the wizard pick the option &quot;Compare prompts and models&quot;.</p><pre id="c80ded9c8d52c31d0a3401f0e3c2f003"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="c80ded9c8d52c31d0a3401f0e3c2f003" data-status="c80ded9c8d52c31d0a3401f0e3c2f003-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="c80ded9c8d52c31d0a3401f0e3c2f003-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">pnpm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">exec</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">promptfoo</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">init</span></div></code></pre><p>You can run the example, or replace the generated YAML configuration with the example above, or adapt it to your own prompts and test cases.
To use TypeScript instead, create <code>promptfooconfig.ts</code> from the TypeScript example.</p><p>Each model provider requires its own credentials. For the OpenAI providers in this example, make <code>OPENAI_API_KEY</code> available in your environment. Keep the key outside the configuration file and do not commit it to source control.</p>
		<h2 id="run-the-tests">
		  <a href="#run-the-tests" class="anchor mark-hover" tabindex="-1">Run the tests</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>For a YAML configuration, run the evaluation from the directory that contains <code>promptfooconfig.yaml</code>:</p><pre id="66eb0a1794e31b9b6db1c7bcd3a5d1e5"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="66eb0a1794e31b9b6db1c7bcd3a5d1e5" data-status="66eb0a1794e31b9b6db1c7bcd3a5d1e5-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="66eb0a1794e31b9b6db1c7bcd3a5d1e5-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">pnpm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">exec</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">promptfoo</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">eval</span></div></code></pre><p>Promptfoo runs every test case against each configured prompt and model. The terminal displays a table with the generated outputs and shows which assertions passed or failed.</p><p>While changing a prompt or its assertions, watch mode automatically starts a new evaluation when the configuration changes:</p><pre id="0e067d3fec6a521eeb4af7aa01399a41"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="0e067d3fec6a521eeb4af7aa01399a41" data-status="0e067d3fec6a521eeb4af7aa01399a41-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="0e067d3fec6a521eeb4af7aa01399a41-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">pnpm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">exec</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">promptfoo</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">eval</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">--watch</span></div></code></pre>
		<h2 id="view-the-results">
		  <a href="#view-the-results" class="anchor mark-hover" tabindex="-1">View the results</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>To inspect the results in the web viewer, run:</p><pre id="dc454f12342ed89a19eb1a145f9194b7"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="dc454f12342ed89a19eb1a145f9194b7" data-status="dc454f12342ed89a19eb1a145f9194b7-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="dc454f12342ed89a19eb1a145f9194b7-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">pnpm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">exec</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">promptfoo</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">view</span></div></code></pre><p>The viewer opens a local web application where you can compare model outputs side by side, inspect failed assertions, and review token usage, latency, and cost. Previous evaluations are also available, which makes it possible to compare the results before and after changing a prompt or model.</p><p>The results can also be written to a standalone HTML file:</p><pre id="0abea6c8d254bfedb5b4626322cb4e4e"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="0abea6c8d254bfedb5b4626322cb4e4e" data-status="0abea6c8d254bfedb5b4626322cb4e4e-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="0abea6c8d254bfedb5b4626322cb4e4e-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">pnpm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">exec</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">promptfoo</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">eval</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">--output</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">results.html</span></div></code></pre><p>This gives you the following results for the example evaluation.</p><figure>
				<img src="https://timdeschryver.dev/blog/testing-ai-prompts-and-comparing-models-with-promptfoo/images/image.webp" alt="" loading="lazy"/>
				<figcaption>Promptfoo's local web viewer compares the results for GPT-5.6 Luna and GPT-5.6 Sol side by side.</figcaption>
			</figure><figure>
				<img src="https://timdeschryver.dev/blog/testing-ai-prompts-and-comparing-models-with-promptfoo/images/image-1.webp" alt="" loading="lazy"/>
				<figcaption>The evaluation details show the result and reason for every assertion.</figcaption>
			</figure>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>In this post, we used promptfoo to test a personal-information extraction prompt with deterministic and model-graded assertions, and to compare the same results across multiple models.
We also saw how to define evaluations in YAML or TypeScript, and how to scale this using a feature-based folder structure and by loading prompts and variables from files.</p><p>Prompt testing is valuable because it turns assumptions about model behavior into repeatable checks.
Start with a few representative inputs and assertions for the most important requirements, then add new test cases whenever an unexpected response or regression is discovered.</p><p>It also helps to reduce costs by comparing models and prompts, and by tracking token usage and latency.
Once the same evaluation suite can run against multiple models, changing models becomes a measurable decision instead of a manual experiment.
You can compare quality alongside token usage, latency, and cost, and choose a smaller or cheaper model with confidence that it still meets the application&#39;s requirements.</p><p>As next steps, the same evaluations can also run as part of a <a class="mark mark-hover" href="https://www.promptfoo.dev/docs/integrations/ci-cd/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.promptfoo.dev)'>CI/CD pipeline</a>, where they can catch prompt or model regressions before a change is deployed. This is very similar to your current test suites.</p><p>Instead of a local workflow, with the setup and files located in a source control repository, you can also opt to use the web version of promptfoo, which provides a shared interface for creating, running, and sharing evaluations. This is also useful to share evaluations within your team on a central location, and to keep track of the evaluation history.
This is available via the Promptfoo app in <a class="mark mark-hover" href="https://www.promptfoo.dev/docs/usage/web-ui/#cloud" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.promptfoo.dev)'>Promptfoo Cloud</a> or a <a class="mark mark-hover" href="https://www.promptfoo.dev/docs/usage/self-hosting/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.promptfoo.dev)'>self-hosted instance</a>.</p><p>Promptfoo can also do more than the prompt and model comparisons covered in this post.
This is definitely worth exploring if AI is heavily integrated into your application, e.g. for <a class="mark mark-hover" href="https://www.promptfoo.dev/docs/red-team/quickstart/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.promptfoo.dev)'>red-teaming and security testing</a> or <a class="mark mark-hover" href="https://www.promptfoo.dev/model-security/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.promptfoo.dev)'>evaluating model security</a>.</p>
		<h2 id="alternatives">
		  <a href="#alternatives" class="anchor mark-hover" tabindex="-1">Alternatives</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Promptfoo is not the only option for evaluating prompts and AI applications.
Depending on the application&#39;s technology stack and whether evaluation needs to be connected to production observability, the following alternatives are also worth considering:</p><ul>
<li><a class="mark mark-hover" href="https://langfuse.com/docs/evaluation/core-concepts" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flangfuse.com)'>Langfuse</a> combines evaluation with tracing, prompt management, datasets, and production observability. It supports offline experiments for comparing prompt or model versions, as well as online evaluations of live application traces, making it a good fit when evaluation should be part of a broader LLM observability workflow. You can also use Langfuse prompts within promptfoo evaluations, which makes it possible to combine the two tools.</li>
<li>The <a class="mark mark-hover" href="https://learn.microsoft.com/en-us/dotnet/ai/evaluation/libraries?WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'>.NET <code>Microsoft.Extensions.AI.Evaluation</code> libraries</a> provide a code-first option for .NET applications. They integrate with test frameworks such as xUnit, NUnit, and MSTest, and include quality, natural language processing (NLP) metrics, safety.</li>
</ul>
]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Your first MCP server with ASP.NET]]></title>
					<description><![CDATA[Learn how to query your application's data using natural language by building an ASP.NET MCP server.]]></description>
					<link>https://timdeschryver.dev/blog/your-first-mcp-server-with-aspnet</link>
					<guid>https://timdeschryver.dev/blog/your-first-mcp-server-with-aspnet</guid>
					<pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/your-first-mcp-server-with-aspnet/images/banner.webp" alt="Your first MCP server with ASP.NET"/><p><a href="https://timdeschryver.dev/blog/your-first-mcp-server-with-aspnet?tldr=true">Read the <strong>code-only version</strong> on timdeschryver.dev</a></p>
		<h2 id="what-and-why">
		  <a href="#what-and-why" class="anchor mark-hover" tabindex="-1">What and Why</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The <a class="mark mark-hover" href="https://modelcontextprotocol.io/introduction" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fmodelcontextprotocol.io)'>Model Context Protocol (MCP)</a> gives AI clients a standardized way to connect with external tools outside of the chat window&#39;s context. These tools can come in different flavors, most commonly they read from external systems to enrich a prompt with structured data.</p><blockquote><span class="icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/></svg></span><p>MCP (Model Context Protocol) is an open-source standard for connecting AI applications to external systems.</p></blockquote><p>Creating your own MCP server allows you to expose your application&#39;s data and logic to AI clients. This seemless integration lets you interact with your application in a more natural way, using natural language instead of technical API calls. If you&#39;re already leveraging AI, it also automates certain tasks, e.g. instead of manually copy-pasting data into a prompt to provide context, the client can call an MCP tool to automatically retrieve the relevant information it needs.
If your tool allows schedudeled tasks, the AI client can also call your tool to perform actions on your behalf.</p><p>With ASP.NET you can create your custom MCP tools using the <a class="mark mark-hover" href="https://www.nuget.org/packages/ModelContextProtocol.AspNetCore/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.nuget.org)'><code>ModelContextProtocol.AspNetCore</code> NuGet package</a>.</p><p>In this post, we&#39;ll build a small HTTP-based MCP tool that exposes a search tool for AI clients, just to give you a quick idea of how it works.</p><p>TLDR: writing an MCP tool is almost identical to building a regular web API endpoint, but it uses the MCP SDK to handle the request and response format for you.</p>
		<h2 id="when-to-use-it">
		  <a href="#when-to-use-it" class="anchor mark-hover" tabindex="-1">When to use it</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Instead of building a traditional (UI) application, you can build an MCP server to expose the internals of your application directly to AI clients. This allows you to leverage the power of AI models to interact with your application. In other words, it gives your prompt a window into your application, so it can retrieve data and perform actions on your behalf.</p><p>This can be useful in many scenarios. Personally, I think it can help to reduce the friction of interacting with your application, and make complex screen flows more accessible. We recently used it to query data by asking &quot;normal&quot; questions instead of building a complex filter UI. It can also be helpful to quickly instruct an action to be performed.</p>
		<h2 id="setup-in-aspnet">
		  <a href="#setup-in-aspnet" class="anchor mark-hover" tabindex="-1">Setup in ASP.NET</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Follow along to create a new ASP.NET application and add the MCP server capabilities to it.</p><p>First start by creating a new ASP.NET application:</p><pre id="680cb98f7f9de1ecf31f86741a9a6d2a"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="680cb98f7f9de1ecf31f86741a9a6d2a" data-status="680cb98f7f9de1ecf31f86741a9a6d2a-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="680cb98f7f9de1ecf31f86741a9a6d2a-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">web</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">-o</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">CatalogMcpServer</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid))">cd</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">CatalogMcpServer</span></div></code></pre><p>In the ASP.NET application, add the <code>ModelContextProtocol.AspNetCore</code> NuGet package:</p><pre id="59d63ac1f78ce0dfe041f2c88d88e42e"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="59d63ac1f78ce0dfe041f2c88d88e42e" data-status="59d63ac1f78ce0dfe041f2c88d88e42e-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="59d63ac1f78ce0dfe041f2c88d88e42e-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">ModelContextProtocol.AspNetCore</span></div></code></pre><p>Then, in the <code>Program.cs</code> file, add the MCP server to the service collection and configure the MCP server to work over HTTP:</p><ul>
<li><code>AddMcpServer()</code> registers the MCP server to the service collection with default options.</li>
<li><code>WithHttpTransport()</code> adds the services necessary for <code>McpEndpointRouteBuilderExtensions.MapMcp</code> to handle MCP requests and sessions using the MCP Streamable HTTP transport.</li>
<li><code>WithToolsFromAssembly()</code> scans the assembly for MCP tool classes (more on this later). If you want, you can also register your tools manually using <code>WithTools&lt;ToolType&gt;()</code> instead.</li>
<li><code>MapMcp()</code> exposes the MCP endpoint over HTTP, and we set it to the path <code>/mcp</code>.</li>
</ul>
<pre id="6d6117c3f457a1c3337edb9cd977308d"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="6d6117c3f457a1c3337edb9cd977308d" data-status="6d6117c3f457a1c3337edb9cd977308d-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="6d6117c3f457a1c3337edb9cd977308d-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    //</span><span style="color: hsl(var(--syntax-comment))"> Adds the Model Context Protocol (MCP) server to the service collection with default options.</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddMcpServer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    //</span><span style="color: hsl(var(--syntax-comment))"> Adds the services necessary for McpEndpointRouteBuilderExtensions.MapMcp to handle MCP requests and sessions using the MCP Streamable HTTP transport.</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithHttpTransport</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    //</span><span style="color: hsl(var(--syntax-comment))"> Manually register your MCP tools.</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithTools</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">CatalogTools</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;();</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    //</span><span style="color: hsl(var(--syntax-comment))"> Adds types marked with the ModelContextProtocol.Server.McpServerToolTypeAttribute attribute from the given assembly as tools to the server.</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithToolsFromAssembly</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapMcp</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;mcp&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre>
		<h2 id="your-first-mcp-tool">
		  <a href="#your-first-mcp-tool" class="anchor mark-hover" tabindex="-1">Your first MCP tool</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>So far, we have created an ASP.NET application and added the MCP server to it. Now let&#39;s create a tool that can be called by AI clients.</p><p>For example, let&#39;s create a tool that allows AI clients to search for products in a catalog.
To keep the example compact, we&#39;re using a in-memory list of products, but in a real application you would query your database or call an external service.</p><p>To create the tool, create a new class <code>CatalogTools</code> and mark it with the <code>McpServerToolType</code> attribute, this is important so it is automatically discovered.</p><p>Within the class create your tool method and mark it with the <code>McpServerTool</code> attribute. The method can be asynchronous and return any serializable type, in this case we return a list of products.</p><pre id="d86eb7a11f3d13d8f247d8bc3b2737ff"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">CatalogTools.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="d86eb7a11f3d13d8f247d8bc3b2737ff" data-status="d86eb7a11f3d13d8f247d8bc3b2737ff-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="d86eb7a11f3d13d8f247d8bc3b2737ff-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">using</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">System</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ComponentModel</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">using</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ModelContextProtocol</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Server</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-entity-name-section))">McpServerToolType</span><span style="color: hsl(var(--syntax-invalid-deprecated))">]</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">sealed</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CatalogTools</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-entity-name-section))">McpServerTool</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Description</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Searches products in the catalog by name.&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)]</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">static</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Task</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">IEnumerable</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">ProductResult</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">SearchProducts</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-entity-name-section))">Description</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Part of the product name, for example 'smartphone'. If not provided, all products will be returned.&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)]</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">query</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-invalid-deprecated))">[]</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ProductResult</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">1</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Keyboard&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">10</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">99.99m</span><span style="color: hsl(var(--syntax-invalid-deprecated))">),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ProductResult</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">2</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Mouse&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">25</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">49.99m</span><span style="color: hsl(var(--syntax-invalid-deprecated))">),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ProductResult</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">3</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Monitor&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">0</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">399.99m</span><span style="color: hsl(var(--syntax-invalid-deprecated))">),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ProductResult</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">4</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Smartphone&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">5</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">899.99m</span><span style="color: hsl(var(--syntax-invalid-deprecated))">),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Where</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">p</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">query</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">==</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">null</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">||</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">p</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Name</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Contains</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">query</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">StringComparison</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">OrdinalIgnoreCase</span><span style="color: hsl(var(--syntax-invalid-deprecated))">));</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">record</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ProductResult</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">int</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">Id</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">Name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">int</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">Stock</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">decimal</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">Price</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div></code></pre><p>As you can see, the tool, and the parameters, are decorated with the <code>Description</code> attribute, which is used by the MCP server to generate a description of the tool and its parameters. This description is sent to the client when it connects, so it knows what tools are available and how to call them.</p><p>A <code>McpServerTool</code> method can have any number of parameters, and the parameters can be of any serializable type. The MCP server will automatically serialize the parameters to JSON and deserialize them back to the method parameters when the tool is called.</p><p>The <code>McpServerToolType</code> can also have multiple tools defined in the same class.</p><p>To test the server, start your application with <code>dotnet run</code>.</p><pre id="246ce7ac125291f7016d1d3267aa6ea0"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="246ce7ac125291f7016d1d3267aa6ea0" data-status="246ce7ac125291f7016d1d3267aa6ea0-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="246ce7ac125291f7016d1d3267aa6ea0-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">run</span></div></code></pre>
		<h3 id="how-it-translates-a-prompt-to-a-request">
		  <a href="#how-it-translates-a-prompt-to-a-request" class="anchor mark-hover" tabindex="-1">How it translates a prompt to a request</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>When the MCP client connects to our server, it asks the server the tools that are available.
In this case it connects to the <code>Catalog</code> server and it discovers the product search tool (<code>search_products</code>).</p><p>When a user asks a question in the chat window, the client uses all of this information to determine which tool to call and with which parameters. It uses the description of the tool and its parameters to understand how to call the tool and provide the right arguments. That&#39;s why it&#39;s important to provide a good description of the tool and its parameters, ideally with some examples, as it helps the client understand how to call the tool and provide the right arguments.</p><p>Let&#39;s take a look at a concrete example, and let&#39;s say we ask the question &quot;Do we sell keyboards?&quot; in the chat window of the AI client:</p><pre id="552a371fceb41e2e22fa1b077c657e21"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="6" width="16" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="11" width="12" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="16" width="16" height="2" rx="1" fill="#64748B"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="552a371fceb41e2e22fa1b077c657e21" data-status="552a371fceb41e2e22fa1b077c657e21-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="552a371fceb41e2e22fa1b077c657e21-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="txt " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-unknown))">Do we sell keyboards?</span></div></code></pre><p>The client detects that the context is about our catalog, and it knows about the Catalog tool. If it doesn&#39;t automatically detect the tool, you can also explicitly tell the client to use the tool.
With that knowledge, it translates the given question into a request that our server expects and understands, in this case it invokes the <code>search_products</code> tool with a JSON payload containing the <code>query</code> parameter set to &quot;keyboard&quot;:</p><pre id="dc186464e71e7da17d21c1a25b9421f0"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.77778 6.66667C4.77778 5.19391 5.97169 4 7.44444 4C7.93536 4 8.33333 4.39797 8.33333 4.88889C8.33333 5.3798 7.93536 5.77778 7.44444 5.77778C6.95353 5.77778 6.55556 6.17575 6.55556 6.66667V10.1675C6.55556 10.8682 6.28251 11.5173 5.82622 12C6.28251 12.4827 6.55556 13.1318 6.55556 13.8325V17.3333C6.55556 17.8243 6.95353 18.2222 7.44444 18.2222C7.93536 18.2222 8.33333 18.6202 8.33333 19.1111C8.33333 19.602 7.93536 20 7.44444 20C5.97169 20 4.77778 18.8061 4.77778 17.3333V13.8325C4.77778 13.4246 4.50018 13.0691 4.10448 12.9701L3.6733 12.8623C3.2776 12.7635 3 12.4079 3 12C3 11.5921 3.2776 11.2365 3.6733 11.1377L4.10448 11.0299C4.50018 10.9309 4.77778 10.5754 4.77778 10.1675V6.66667ZM19 6.66667C19 5.19391 17.8061 4 16.3333 4C15.8424 4 15.4444 4.39797 15.4444 4.88889C15.4444 5.3798 15.8424 5.77778 16.3333 5.77778C16.8243 5.77778 17.2222 6.17575 17.2222 6.66667V10.1675C17.2222 10.8682 17.4953 11.5173 17.9516 12C17.4953 12.4827 17.2222 13.1318 17.2222 13.8325V17.3333C17.2222 17.8243 16.8243 18.2222 16.3333 18.2222C15.8424 18.2222 15.4444 18.6202 15.4444 19.1111C15.4444 19.602 15.8424 20 16.3333 20C17.8061 20 19 18.8061 19 17.3333V13.8325C19 13.4246 19.2776 13.0691 19.6733 12.9701L20.1044 12.8623C20.5002 12.7635 20.7778 12.4079 20.7778 12C20.7778 11.5921 20.5002 11.2365 20.1044 11.1377L19.6733 11.0299C19.2776 10.9309 19 10.5754 19 10.1675V6.66667Z" fill="#8B5CF6"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="dc186464e71e7da17d21c1a25b9421f0" data-status="dc186464e71e7da17d21c1a25b9421f0-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="dc186464e71e7da17d21c1a25b9421f0-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="json " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">query</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;keyboard&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>ASP.NET receives the tool call, the MCP SDK invokes <code>SearchProducts</code>, and the client receives the result.
The model then uses that result to formulate a suitable answer.</p><p>Based on the result the model either:</p><ul>
<li>Answers the question directly when it has enough information;</li>
<li>Uses the result to extract the relevant information and answer the question;</li>
<li>Invokes other tools until it has enough information to answer the question.</li>
</ul>
<p>Based on the question and the result, the AI model decides how to structure and format the answer in a way that is most useful to the user. It can for example just answer the question, or display a collection as a table.</p><p>In the case of our example, the model uses the result to answer the question directly:</p><pre id="3e10a61ef65cc2efd93291d60e583708"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="6" width="16" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="11" width="12" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="16" width="16" height="2" rx="1" fill="#64748B"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="3e10a61ef65cc2efd93291d60e583708" data-status="3e10a61ef65cc2efd93291d60e583708-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="3e10a61ef65cc2efd93291d60e583708-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="txt " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-unknown))">Yes, we sell keyboards</span></div></code></pre><p>Or it can even add extra information, like the stock and price of the product:</p><pre id="ac437a3f96d8f022322a7d06ce64c406"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="6" width="16" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="11" width="12" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="16" width="16" height="2" rx="1" fill="#64748B"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="ac437a3f96d8f022322a7d06ce64c406" data-status="ac437a3f96d8f022322a7d06ce64c406-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="ac437a3f96d8f022322a7d06ce64c406-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="txt " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-unknown))">Yes — we sell Keyboard.</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">    Stock: 10</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">    Price: $99.99</span></div></code></pre>
		<h2 id="installing-the-mcp-server-with-an-ai-client">
		  <a href="#installing-the-mcp-server-with-an-ai-client" class="anchor mark-hover" tabindex="-1">Installing the MCP server with an AI client</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>To make the MCP server available to AI clients, we need to add the server to the list of available MCP servers. Depending on the AI client you are using, this can be done in different ways. Some clients allow you to add the server to a configuration file, while others allow you to add it via a UI (this can also be named plugins, extensions, ...).</p><p>In the following example, we add a new entry to the <code>.mcp.json</code> file in the root of the project, as this file is generic and can be used by multiple AI clients (GitHub Copilot, Codex, OpenCode) to discover available MCP servers.</p><pre id="9d83dbf21e0866bd0e8bc7d72f17e59f"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.77778 6.66667C4.77778 5.19391 5.97169 4 7.44444 4C7.93536 4 8.33333 4.39797 8.33333 4.88889C8.33333 5.3798 7.93536 5.77778 7.44444 5.77778C6.95353 5.77778 6.55556 6.17575 6.55556 6.66667V10.1675C6.55556 10.8682 6.28251 11.5173 5.82622 12C6.28251 12.4827 6.55556 13.1318 6.55556 13.8325V17.3333C6.55556 17.8243 6.95353 18.2222 7.44444 18.2222C7.93536 18.2222 8.33333 18.6202 8.33333 19.1111C8.33333 19.602 7.93536 20 7.44444 20C5.97169 20 4.77778 18.8061 4.77778 17.3333V13.8325C4.77778 13.4246 4.50018 13.0691 4.10448 12.9701L3.6733 12.8623C3.2776 12.7635 3 12.4079 3 12C3 11.5921 3.2776 11.2365 3.6733 11.1377L4.10448 11.0299C4.50018 10.9309 4.77778 10.5754 4.77778 10.1675V6.66667ZM19 6.66667C19 5.19391 17.8061 4 16.3333 4C15.8424 4 15.4444 4.39797 15.4444 4.88889C15.4444 5.3798 15.8424 5.77778 16.3333 5.77778C16.8243 5.77778 17.2222 6.17575 17.2222 6.66667V10.1675C17.2222 10.8682 17.4953 11.5173 17.9516 12C17.4953 12.4827 17.2222 13.1318 17.2222 13.8325V17.3333C17.2222 17.8243 16.8243 18.2222 16.3333 18.2222C15.8424 18.2222 15.4444 18.6202 15.4444 19.1111C15.4444 19.602 15.8424 20 16.3333 20C17.8061 20 19 18.8061 19 17.3333V13.8325C19 13.4246 19.2776 13.0691 19.6733 12.9701L20.1044 12.8623C20.5002 12.7635 20.7778 12.4079 20.7778 12C20.7778 11.5921 20.5002 11.2365 20.1044 11.1377L19.6733 11.0299C19.2776 10.9309 19 10.5754 19 10.1675V6.66667Z" fill="#8B5CF6"/>
</svg>
 <span class="file-name">.mcp.json</span> <button type="button" class="copy-code icon align-text-top" data-ref="9d83dbf21e0866bd0e8bc7d72f17e59f" data-status="9d83dbf21e0866bd0e8bc7d72f17e59f-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="9d83dbf21e0866bd0e8bc7d72f17e59f-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="json " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">mcpServers</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">catalog</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;http&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">url</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&lt;ASP.NET URL&gt;/mcp&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>Because we made the MCP server available over HTTP, we register it as an HTTP server with the URL of the endpoint we mapped in <code>Program.cs</code>.</p><p>After adding the server to the configuration file, you might also need to &quot;start&quot; the MCP tool.
Again, this is different depending on the tools you are using, and whether you are registering the MCP server as part of a project workspace or if it&#39;s available globally.</p><p>In VS Code, there will be a lightbulb above the &quot;catalog&quot; entry in the <code>.mcp.json</code> file, click &quot;Start&quot; to start the server and make it available to the chat window of VS Code.</p><p>After these steps, you should see that the client can discover the catalog MCP server, together with the available tools and their descriptions.</p><figure>
				<img src="https://timdeschryver.dev/blog/your-first-mcp-server-with-aspnet/images/tool.webp" alt="" loading="lazy"/>
				<figcaption>The search_products tool shown as part of the CatalogMcpServer</figcaption>
			</figure>
		<h2 id="result">
		  <a href="#result" class="anchor mark-hover" tabindex="-1">Result</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>You can now ask questions about the catalog products, and the client should call the <code>search_products</code> tool to retrieve the relevant information.</p><figure>
				<img src="https://timdeschryver.dev/blog/your-first-mcp-server-with-aspnet/images/chat.webp" alt="" loading="lazy"/>
				<figcaption>A chat window asking questions about the catalog products</figcaption>
			</figure><p>In the chat history you can notice that the client calls the <code>search_products</code> tool, and if you click on it you can also verify the raw input and outputs. The model then uses that result to formulate a suitable answer.</p>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>In this blog post, we&#39;ve built a small HTTP-based MCP server with ASP.NET that gives you a quick idea of how you can build your own MCP server, and how AI clients communicate with it.</p><p>MCP allows AI clients to use external systems outside of the chat window&#39;s context.
A common use case is to retrieve relevant data from an external system.</p><p>This can be useful for your application, if you want to expose your application&#39;s data so your users can inquire about it in a more natural way via a chat window.
While this was a simple example, in a real-world scenario, you would query your database or call an external service to retrieve the relevant information.</p><p>The next steps are to secure the MCP server with authentication. Since it&#39;s just an ASP.NET application, you can re-use the same techniques you would use to secure any other Web API application. You could for example add a Key header to the request and validate it using middleware.</p><p>Instead of making the MCP tool available via HTTP, you can also make it available as a CLI tool using <code>WithStdioServerTransport()</code> instead of <code>WithHttpTransport()</code>. This allows you to run the MCP server as a separate process and communicate with it via standard IO.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Using Agentic AI to create your own component library]]></title>
					<description><![CDATA[Agentic AI changes the trade-off between using an existing component library and building your own. This post explores how AI agents can help teams create, maintain, and evolve a custom Angular component library without taking on the full cost.]]></description>
					<link>https://timdeschryver.dev/blog/using-agentic-ai-to-create-your-own-component-library</link>
					<guid>https://timdeschryver.dev/blog/using-agentic-ai-to-create-your-own-component-library</guid>
					<pubDate>Tue, 09 Jun 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/using-agentic-ai-to-create-your-own-component-library/images/banner.webp" alt="Using Agentic AI to create your own component library"/><p>Read <strong>Using Agentic AI to create your own component library</strong> on <a href="https://timdeschryver.dev/blog/using-agentic-ai-to-create-your-own-component-library">timdeschryver.dev</a></p><p>Starting a new frontend project always comes with a set of decisions that shape the project in the long term.
One of those key decisions is whether the team should use an existing component library or build its own.</p><p>For a long time, my default recommendation was simple: use an existing component library.</p><p>That advice still makes sense in many cases. Existing component libraries give you speed. They provide ready-to-use components with consistent behavior, (hopefully) accessibility considerations, and documentation including examples. For many teams, especially smaller teams, this is the most pragmatic choice.</p><p>But that choice also comes with a cost.</p><p>When you adopt a component library, you also adopt its assumptions. You get its generic API design, its look and feel, and a predefined behavior. You&#39;re also dependent on its release cycle and upgrade path, or lack of upgrade path. That is not necessarily a problem, but it&#39;s something to be aware of.</p><p>In practice, this friction doesn&#39;t always appear immediately.</p><p>At the start of a project, a component library usually feels like a productivity boost. You install it, add a few components, and quickly build screens that look consistent. Because most libraries have a lot of extra bells and whistles, you can often get a lot of functionality with minimal effort. You start to use it extensively, but as the product grows, the design requirements become more specific, and more custom to your needs. The product team wants a slightly different interaction than the standard. A flow requires specific behavior that does not fit the component&#39;s generic design.</p><p>When this happens, teams start to bend the component library to their specific needs, and this is where the cost starts to appear, and if done poorly, it can become a significant maintenance burden. Teams start to customize the library to fit their needs, using various techniques:</p><ul>
<li>they override CSS</li>
<li>they create wrapper components to add missing features or to implement their own API on top of the library&#39;s API using their own defaults</li>
<li>they reach into internals to tweak behavior</li>
</ul>
<p>Teams do this to work around behavior that was not designed for their use case.</p><p>While this is not necessarily a problem, sometimes it is fine. But over time, those customizations can become expensive.</p><p>The more you deviate from the library&#39;s intended usage, the harder it becomes to upgrade. What started as a productivity gain can slowly turn into a dependency that is difficult to change. A major version update can quickly break a project. Every internal CSS class you relied on becomes a risk. Every wrapper component becomes a translation layer between your application and the library. This is when an upgrade path can become a long and expensive process, that requires buy-in from above.</p><div class="custom-block info">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z"/><path d="M15 3v4a2 2 0 0 0 2 2h4"/></svg></span>Note</div>
			<p>A component adapter is a good idea if designed intentionally, and if a team takes ownership of it. But it can also become a source of technical debt if it is just a collection of hacks on top of hacks.</p>
		</div><p>This does not mean component libraries are bad. They are not, and they remain a good choice for many projects.
But the decision is not free. You are trading control for convenience, and that trade-off can become more expensive the more your product diverges from the library&#39;s assumptions.</p><p>In this post I want elaborate that my point is not that every team should build its own component library. My point is that agentic AI lowers the cost of doing so, especially when combined with a source-owned component approach.</p>
		<h2 id="the-traditional-trade-off">
		  <a href="#the-traditional-trade-off" class="anchor mark-hover" tabindex="-1">The traditional trade-off</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>When teams face this decision, the decision is often framed as a trade-off between control and convenience:</p><ul>
<li>Using an existing component library gives you speed and stability, but less control.</li>
<li>Building your own component library gives you control and flexibility, but more responsibility, with a higher initial cost.</li>
</ul>
<p>That responsibility is significant. A component library is not just a collection of buttons, inputs, and other UI elements. It is an internal product. It needs a good and consistent API, accessibility features, test coverage, documentation, and maintenance.</p><p>For smaller teams, that cost is often too high.</p><p>That is why I would usually advise against building your own component library unless there is a strong reason to do so. Most teams should not spend their time reinventing components when their actual goal is to build business value.</p><p>Over the past years, a third option has become more valid with the rise of utility-first CSS frameworks, and more specifically with <a class="mark mark-hover" href="https://tailwindcss.com/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Ftailwindcss.com)'>Tailwind CSS</a>. This recent frontend ecosystem shift has provided a good alternative to the traditional component library model.</p><p>A useful middle ground has emerged, and instead of consuming a component library as a black-box dependency, you pull the component source code into your own codebase. Instead of installing a component package within your node modules and treating it as an external runtime dependency, you copy the component code directly into your project as your own components. From that point on, you own the code, and you can modify it as needed.</p><p>This way of working, also called a code distribution platform, is successfully implemented and popularized by libraries such as <a class="mark mark-hover" href="https://ui.shadcn.com/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fui.shadcn.com)'>shadcn/ui</a> in the React ecosystem, and <a class="mark mark-hover" href="https://spartan.ng/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fspartan.ng)'>Spartan</a> or <a class="mark mark-hover" href="https://zardui.com/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fzardui.com)'>Zard</a> in the Angular ecosystems.</p><p>That model is interesting because it changes the ownership boundary.</p><p>You still avoid writing everything from scratch, and you get a solid foundation as a starting point. But you are not limited by an external package&#39;s public API, styling decisions, or release cycle. You can adapt the component to your application/product because you own the code. This gives you the flexibility to customize and extend behavior without fighting the library.</p>
		<h2 id="agentic-ai-to-increase-the-viability-of-custom-component-libraries">
		  <a href="#agentic-ai-to-increase-the-viability-of-custom-component-libraries" class="anchor mark-hover" tabindex="-1">Agentic AI to increase the viability of custom component libraries</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The biggest reason not to build your own component library has always been the time it requires to create and maintain it. This is time that is not spent building product features, and it is time that is not directly visible to stakeholders. This makes it a cost that can be difficult to justify.</p><p>Writing the first version is only part of the work. The real cost comes later: improving APIs, fixing edge cases, keeping components accessible, adapting them to new design requirements, adding tests, keeping it up to date with the latest Angular version and standards, and documenting its usage.</p><p>That cost does not disappear with AI, but it changes.</p><p>Agentic AI can help you with the repetitive, structural, and incremental work that made custom component libraries expensive in the past. An AI agent can generate a first implementation, refactor an API, add tests, improve accessibility, migrate code to a newer Angular pattern, or create examples based on existing components.</p><p>This does not mean that you can blindly outsource your component library to an AI agent. You still need technical ownership. You still need engineers who understand the architecture, review the output, and define the standards.</p><p>But the amount of effort required to create and evolve components is lower than it used to be.</p><p>That disrupts the traditional trade-off. The convenience of an existing component library is still there, but the cost of ownership for a custom component library is now lower. This makes the option of building your own more viable for more teams.</p><p>For some teams, building a custom component library is no longer an unrealistic investment. It can become a practical option, especially when done incrementally.</p><p>Instead of deciding upfront that you are going to build a complete component library, you can start with the components you actually need. With this incremental approach, you can let real application needs drive the component library. You can create components as patterns emerge in the application, and you can use AI agents to help with the implementation and maintenance work, one component at a time.</p><p>Take the AI-driven approach in combination with code distribution platforms, and it also works well. You can pull in the source code for a component, for the initial implementation, and then use AI agents to help customize it to your needs, and maintain it over time. You get the best of both worlds: a solid starting point and the flexibility to evolve it as needed. When the component doesn&#39;t fit your needs, you can also decide to rewrite it from scratch.</p>
		<h2 id="benefits-of-owning-your-components">
		  <a href="#benefits-of-owning-your-components" class="anchor mark-hover" tabindex="-1">Benefits of owning your components</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The main benefit of owning your components is control, and flexibility over styling, behavior, and API design.</p>
		<h3 id="customization-without-fighting-the-library">
		  <a href="#customization-without-fighting-the-library" class="anchor mark-hover" tabindex="-1">Customization without fighting the library</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Custom styling is one of the main sources of friction when using external component libraries.</p><p>Most teams eventually need to make components look and behave differently from the default. Some libraries provide strong theming support. Others are harder to customize. Even when customization is possible, you are still working within the constraints of the library&#39;s design.</p><p>Owning the component source code avoids much of that friction.</p><p>You do not need to override internal CSS selectors. You do not need to wrap a component just to change a small part of its template. You do not need to wait for a feature request to be accepted upstream.</p><p>You can change the component.</p><p>That does not mean every component should become a one-off implementation. The goal is still consistency. The difference is that the consistency is defined by your team instead of by an external package.</p><p>This is especially useful when you have a strong design system or a product with specific interaction patterns.</p><p>Your component library can reflect your product directly.</p>
		<h3 id="control-over-api-design">
		  <a href="#control-over-api-design" class="anchor mark-hover" tabindex="-1">Control over API design</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>One of the most underestimated benefits of owning your components is control over the API surface.</p><p>A good component API should match how your team builds applications.</p><p>External component libraries need to support many use cases. That often leads to broad APIs with many inputs, configuration options, slots, templates, and extension points. This flexibility is necessary for a public library, but it can also make components harder to use.</p><p>An internal component library has a different goal.</p><p>It does not need to support every possible use case. It only needs to support your use cases.</p><p>That means your APIs can be smaller, more opinionated, and more aligned with your product. You can encode your design system directly into the components. You can remove options that should not be configurable. You can make the default path the correct path.</p><p>For example, instead of exposing every possible option, you can design a component API that matches your product language. Only if it&#39;s necessary, you can add a more flexible API for edge cases. But the main API can be simple and focused.</p><p>The component becomes part of your application architecture.</p><p>With agentic AI, you can also iterate on this API faster. You can ask an agent to inspect existing usage, identify inconsistencies, suggest a simpler API, and update all call sites. That kind of refactoring used to be tedious. It still requires review, but it is no longer as expensive.</p>
		<h2 id="how-to-make-it-work">
		  <a href="#how-to-make-it-work" class="anchor mark-hover" tabindex="-1">How to make it work</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>You can build your own component library from scratch, or use a code distribution platform to pull in the source code of existing components. But this doesn&#39;t affect the core principles of how to make it work. The most important part is clear ownership over the components.</p>
		<h3 id="incremental-adoption">
		  <a href="#incremental-adoption" class="anchor mark-hover" tabindex="-1">Incremental adoption</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>A common mistake is thinking about a component library as a large upfront investment.</p><p>That does not have to be the case.</p><p>A more practical approach is to build the library incrementally. Start with the smallest useful set of components and only add more when the application needs them. When creating the component don&#39;t try to predict every possible use case. Instead, focus on the most common patterns in your application and build components that support those patterns well.</p><p>This keeps the cost low.</p><p>You do not need to spend weeks up-front designing a complete library before building product features. You can extract components as more use cases appear. You can let the application needs drive the component creation and evolution over time.</p><p>This also prevents overengineering.</p><p>Many internal component libraries fail because they try to predict too much too early. They create abstractions for use cases that never appear. They introduce flexibility that nobody needs. This results in the library becoming a project inside the project.</p><p>A better approach is to start small.</p><p>Create components when duplication appears. Stabilize the API when usage becomes clear. Use AI agents to help with implementation, tests, documentation, and migrations.</p><p>This gives you control without turning the component library into a bottleneck.</p>
		<h3 id="treat-it-as-an-external-dependency">
		  <a href="#treat-it-as-an-external-dependency" class="anchor mark-hover" tabindex="-1">Treat it as an external dependency</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Even when the component source code lives in your repository, it helps to mentally treat the library as an external dependency to overcome the temptation to reach into internals.</p><p>That means application developers should not need to understand every internal detail of every component. The public API should be the main contract.</p><p>This can come across as bad advice. After all, you own the code and are responsible for it. If things go wrong, it&#39;s on you. But if the public API is designed properly, it gives you a stable surface to build on, while still allowing you to change the internals as needed, for example, when it becomes a problem.</p><p>This allows you to move fast, without discussing every internal detail every time you need to use a component.</p><p>When a component is well-designed, it should allow the internals to change, without affecting the public API.
If the component behaves correctly, it shouldn&#39;t matter if the implementation can be slightly refactored.
At least that&#39;s my opinion. I&#39;m confident that when the component is small, that it can be refactored or rewritten without affecting the application code.</p><p>AI agents can help here as well. They can generate usage examples, detect breaking API changes, update call sites, and keep documentation aligned with implementation.</p>
		<h3 id="accessibility-still-requires-ownership">
		  <a href="#accessibility-still-requires-ownership" class="anchor mark-hover" tabindex="-1">Accessibility still requires ownership</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>One of the risks of building your own component library is accessibility.</p><p>External component libraries often have accessibility built in, or at least they have spent time handling keyboard behavior, ARIA attributes, focus management, and screen reader support. When you build your own components, that responsibility moves to your team.</p><p>Again, AI can help here.
An agent can suggest ARIA attributes, implement keyboard interactions, and compare components against accessibility patterns. That is useful. But accessibility is not something to blindly delegate.</p><p>You still need to validate behavior.</p><p>For Angular projects, a useful building block is the new <a class="mark mark-hover" href="https://angular.dev/guide/aria/overview" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'><code>@angular/aria</code> package</a>, which provides accessibility primitives that can be used in your components. These primitives are designed to follow best practices and can help reduce the risk of accessibility issues. The aria package provides directives to handle accessibility patterns, without affecting the visual styling. This allows you to create custom-styled components while still maintaining accessibility.</p><blockquote><span class="icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/></svg></span><p>Angular Aria works well when you need accessible interactive components that are WCAG compliant with custom styling.</p></blockquote><p>Examples include components such as autocompletes, selects, comboboxes, menus, accordions, and more. These components require more than visual styling. They need correct focus management, keyboard support, roles, labels, right-to-left languages, and interaction semantics.</p><p>These are exactly the areas where a custom component library can become risky if the team underestimates the complexity.</p><p>A valid approach is to combine AI assistance with established accessibility primitives, automated tests, manual verification, and code review.</p>
		<h2 id="where-ai-agents-help-most">
		  <a href="#where-ai-agents-help-most" class="anchor mark-hover" tabindex="-1">Where AI agents help most</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Agentic AI is useful because component libraries contain a lot of work that is structured and well-defined.
It&#39;s also something very common across many teams, which means that the agent has enough training data to learn from, and enough context to understand the patterns.</p><p>For example, a coding agent can help to:</p><ul>
<li>generate an initial component implementation;</li>
<li>create tests with interaction scenarios;</li>
<li>add and validate accessibility features, such as ARIA attributes and keyboard support;</li>
<li>write usage examples;</li>
<li>update documentation;</li>
<li>refactor APIs;</li>
<li>migrate components to newer Angular APIs;</li>
<li>align similar components with the same conventions;</li>
<li>scan for inconsistent usage across the application.</li>
</ul>

		<h3 id="how-to-set-up-the-agent-for-success">
		  <a href="#how-to-set-up-the-agent-for-success" class="anchor mark-hover" tabindex="-1">How to set up the agent for success</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>To improve the quality and consistency of the output, it is important to provide the agent with enough context and constraints.
If you want to use AI agents to help build a component library, define the rules first.</p><p>Use the <a class="mark mark-hover" href="https://angular.dev/ai/agent-skills" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'>Angular Agent Skills</a> to give the agent a good understanding of Angular concepts and best practices. This will help the agent generate code that is consistent and qualitive. Also create your own design system skill, that explains your design system and conventions. This will help the agent generate components that fit your product&#39;s style, design tokens (e.g. colors, typography, spacing), and requirements. If you have team rules, also include these in a SKILL or add it to the instruction file, this can touch on folder structure, naming conventions, testing requirements, documentation format, and more.</p><p>These standards do not need to be perfect from day one. But they need to exist.
AI agents are much more useful when they are constrained. A clear set of conventions improves the quality and consistency of the output. The less code, the better.</p><p>With this in place, when you ask the agent to create a new component, it has a target to follow, improving the overall quality and consistency of the generated code.</p>
		<h2 id="when-not-to-build-your-own">
		  <a href="#when-not-to-build-your-own" class="anchor mark-hover" tabindex="-1">When not to build your own</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Even with AI agents, building your own component library is not always the right choice.</p><p>An existing component library is still a better option when your team needs broad component coverage quickly, does not have strong design requirements, or cannot commit to owning UI infrastructure.</p><p>Complex components are also worth considering carefully.</p><p>For example, data grids, rich text editors, and charts are harder than they look. They contain many edge cases, performance considerations, and interaction details.</p><p>In those cases, using a specialized existing library may still be the better decision.
The point is not to reject external dependencies. The point is to be intentional about where you want control.</p><p>You can also combine and mix both approaches.
Use custom components for your core design system. Use existing libraries for complex specialized components. Wrap external dependencies only when the wrapper gives you a clear boundary and does not fight the underlying library.</p>
		<h2 id="a-practical-workflow">
		  <a href="#a-practical-workflow" class="anchor mark-hover" tabindex="-1">A practical workflow</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>If you decide to build your own component library, the next question is how to do it in a way that is sustainable.
A practical workflow for building an AI-assisted Angular component library could look like this.</p><p>Start by creating a dedicated library or folder for shared UI components. Define the public API and conventions. Add a few foundational pieces such as buttons, icons, form fields, labels, error messages, and layout primitives. You can also use existing libraries as a starting point, by pulling in the source code for some components, and then customizing them to fit your design system and API conventions.</p><p>Then use real product work to drive the next components.</p><p>When screens and flows need a repeated pattern, extract it. Ask the agent to generate the component based on existing conventions. Review the API before reviewing the implementation. Make sure the component is easy to consume.</p><p>Then ask the agent to add tests, examples, and documentation.</p><p>For accessibility-sensitive components, consider using Angular accessibility primitives where possible.</p><p>For more complex components, decide to add a dependency on an existing library. While choosing a library, consider that it fits your design system so it doesn&#39;t look out of place.</p><p>Finally, keep the component library healthy. When one component improves, let the agent inspect similar components and suggest consistent changes. When Angular introduces a new recommended pattern, use the agent to help migrate the library incrementally.</p><p>This creates a feedback loop.</p><p>The application drives the component library. The component library improves the application. The AI agent reduces the maintenance cost between both.</p>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Choosing between an existing component library and writing a custom one used to be a fairly predictable decision.</p><p>For most teams, the existing library was the safer and faster choice. Building your own meant taking on too much implementation and maintenance work.</p><p>Agentic AI changes that. While code distribution platforms already made it more viable to build your own component library, AI agents make it more practical to maintain and evolve it over time.</p><p>AI can reduce the cost of creating components, but it does not remove the need for engineering ownership. Without standards, reviews and tests, it only helps you generate technical debt faster.</p><p>By taking on the cost of ownership you gain control and flexibility. You can create components that are tailored to your product&#39;s design and interaction patterns. An incremental component library becomes a realistic option for more teams.</p><p>This isn&#39;t a &quot;or-decision&quot; but a &quot;both-and&quot; approach. You can use existing libraries where it makes sense, use code distribution platforms to pull in source code for a solid starting point, use <code>@angular/aria</code> for accessibility primitives, use external libraries for complex components, and build your own where you see the most value in owning the code.</p><p>For Angular teams, this can be especially useful. You stay within the Angular ecosystem, rely on Angular migrations, own your API surface, and avoid being blocked by external library decisions.</p><p>The result is not that every team should build everything themselves.</p><p>The better conclusion is this: build where control matters, reuse where complexity is high, and let AI agents reduce the cost of maintaining the pieces you decide to own.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Running a production-like local environment with Aspire]]></title>
					<description><![CDATA[Prevent integration surprises in production by using Aspire to run local dependencies in a consistent, production-like setup, with Azure Blob Storage and Azurite as a practical example.]]></description>
					<link>https://timdeschryver.dev/blog/running-a-production-like-local-environment-with-aspire</link>
					<guid>https://timdeschryver.dev/blog/running-a-production-like-local-environment-with-aspire</guid>
					<pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/running-a-production-like-local-environment-with-aspire/images/banner.webp" alt="Running a production-like local environment with Aspire"/><p>Read <strong>Running a production-like local environment with Aspire</strong> on <a href="https://timdeschryver.dev/blog/running-a-production-like-local-environment-with-aspire">timdeschryver.dev</a></p><p>Setting up a local development environment that behaves like production is often harder than it should be.</p><p>The application code might run fine locally, but the surrounding services are usually where the differences and problems appear. Databases, queues, caches, storage accounts, mail servers, identity providers and external APIs all have their own setup, configuration and lifecycle.</p><p>In many projects, this leads to (small) differences between local development and deployed environments. Those differences are easy to miss while building a feature. They usually become visible later, during testing, deployment or in the worst-case scenario, production environments.</p><p>The most common example is probably the database as it&#39;s a critical dependency for most applications.
In my early days, a common approach was to use a shared development database for the entire team. That worked, but it also created conflicts. Developers could overwrite each other&#39;s data, migrations could get out of sync, and one small schema change could affect everyone else, it could even break the entire environment for everyone. This wasn&#39;t ideal, as it was a fragile setup that required constant communication and coordination.
Later, with the popularity of containerization, local database instances became more common. That improved things because you could work in isolation, but it still required manual setup that every developer had to be aware of. You had to remember to run the database container, ensure it was up to date with the latest migrations, and handle any issues that came up with the local instance.</p><p>Other dependencies were even more challenging to run locally.
Think about:</p><ul>
<li>mail systems</li>
<li>blob storage</li>
<li>queues</li>
<li>caching layers</li>
<li>identity providers</li>
<li>external HTTP services</li>
</ul>
<p>Because many of these services were difficult to replicate, we would implement workarounds to run the code.
Some integrations could be disabled, teams relied on mocks and stubs, or used a shared resource (with the same issues as the shared database) to get by. Another possibility could be to update data, e.g. updating email addresses to prevent emails from being sent to real users in non-production environments.</p><p>While this works, it lowers the confidence that solutions that work locally also work in production environments.
You are no longer testing the application in a context that behaves close enough to the real one.
That can lead to surprises later on, when the code interacts with the real dependencies and the differences become apparent.</p><p>Because these workarounds were often implemented in an ad-hoc way within the application, they also added complexity to the codebase. It could even lead to accidental mistakes causing a big impact if gone wrong, where local environments accidentally hit production integrations, for example during debugging sessions.
Or the other way around, when code running in production is not using the production systems.</p>
		<h2 id="where-aspire-helps">
		  <a href="#where-aspire-helps" class="anchor mark-hover" tabindex="-1">Where Aspire helps</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Aspire gives you a way to describe your application and its dependencies from one place: the <code>AppHost</code>.</p><p>Within the <code>AppHost</code>, you define the services your application needs. Aspire can then orchestrate those services for local development and wire the required connection information into the consuming projects.</p><p>That does not make your local environment identical to production, but it does make it much easier to run a production-like environment on your machine. It can also help to document the dependencies of your application, and visualize how the different pieces of a system interact with each other.</p><p>That distinction matters. A local emulator is still an emulator. A containerized dependency is still not the same as a managed cloud service. But for day-to-day development, it gives you a much better feedback loop than manually configured shared resources or incomplete mocks. This results in more confidence in your code and fewer surprises when you deploy.</p><p>Later on, that same application model can be published to a non-local environment, being a staging environment or production environment.</p><p>While the aspire team manages many integrations to help you run local integrations, Aspire also provides the flexibility to write your own custom integrations for any service you need. With that flexibility in place, it makes it possible that there is also a growing ecosystem of community integrations (thank you <a class="mark mark-hover" href="https://github.com/CommunityToolkit/Aspire" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>CommunityToolkit</a>) to run a wide variety of dependencies locally.</p><p>Some typical examples:</p><ul>
<li>Databases: use the SQL Server or PostgreSQL integration to easily set up a local instance of your database with automated migrations, ensuring that you are always working with the latest schema changes.</li>
<li>Caching: use the Redis integration to set up a local caching layer.</li>
<li>Azure resources: use the Azure Storage integration to set up local instances using Azurite to emulate Azure Storage services.</li>
<li>Mailing systems: use the Mailpit integration to set up a SMTP server for testing email functionality without sending emails to external recipients.</li>
<li>Observability: if you want to use something different than the built-in dashboard, you can use the Elastic Stack or Seq integrations to set up local instances of these popular observability tools.</li>
<li>Messaging: use the RabbitMQ, Kafka, or Azure Service Bus integrations to set up local instances of these messaging systems for testing and development.</li>
<li>Identity providers: use the IdentityServer integration with Keycloak or Zitadel.</li>
</ul>
<div class="custom-block tip">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 14c.2-1 .7-1.7 1.5-2.5A4.8 4.8 0 0 0 18 8 6 6 0 0 0 6 8c0 1.3.5 2.5 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg></span>Tip</div>
			<p>The full list changes rapidly, so the <a class="mark mark-hover" href="https://aspire.dev/integrations" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Faspire.dev)'>Aspire integrations documentation</a> is the best place to check what is currently available.</p>
		</div>
		<h2 id="example-azure-blob-storage-with-azurite">
		  <a href="#example-azure-blob-storage-with-azurite" class="anchor mark-hover" tabindex="-1">Example: Azure Blob Storage with Azurite</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Let&#39;s make this more concrete with an integration of Azure Blob Storage as an example.</p><p>In this scenario, the application interacts with Azure Blob Storage to store and retrieve files. In production, this connects to the actual Azure Storage account. However, for local development, you want to avoid using shared cloud resources due to the isolation level, and potential costs.</p><p>With Aspire, you can model the Azure Storage dependency in the <code>AppHost</code> and run it locally through Azurite.</p><p>First, add the Azure Storage hosting integration to your AppHost project:</p><pre id="00c2dc1d46359f41a4a5d83e962a14ea"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="00c2dc1d46359f41a4a5d83e962a14ea" data-status="00c2dc1d46359f41a4a5d83e962a14ea-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="00c2dc1d46359f41a4a5d83e962a14ea-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">aspire</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">azure-storage</span></div></code></pre><p>This will install the <code>Aspire.Hosting.Azure.Storage</code> package, which allows Aspire to connect and use the Azure Storage services.</p><p>Then define the storage resource in the <code>AppHost.cs</code> file:</p><pre id="bd6788c0631a8bb9a68f3cd3ce1db075"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">AppHost.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="bd6788c0631a8bb9a68f3cd3ce1db075" data-status="bd6788c0631a8bb9a68f3cd3ce1db075-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="bd6788c0631a8bb9a68f3cd3ce1db075-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">DistributedApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">storage</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddAzureStorage</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;storage&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">RunAsEmulator</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">azurite</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">azurite</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithLifetime</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">ContainerLifetime</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Persistent</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithDataVolume</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">blobs</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">storage</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddBlobs</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;blob-storage&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">filesContainer</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">storage</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddBlobContainer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;app-files&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">blobContainerName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;app-files&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProject</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Projects</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Project_Api</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;project-api&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithReference</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">blobs</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WaitFor</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">filesContainer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>This does a few things:</p><ul>
<li>It defines an Azure Storage resource called <code>storage</code>.</li>
<li>It runs that resource locally through Azurite.</li>
<li>It adds a blob service resource called <code>blobs</code>.</li>
<li>It adds a specific blob container called <code>app-files</code>.</li>
<li>It references the blob service from the API project, so the API receives the required connection information through Aspire as environment variables.</li>
</ul>
<p>The <code>WithLifetime(ContainerLifetime.Persistent)</code> and <code>WithDataVolume()</code> calls are useful during development. They make sure the Azurite container and its data survive across local runs, instead of starting from an empty state every time.</p>
		<h2 id="consuming-blob-storage-from-the-api">
		  <a href="#consuming-blob-storage-from-the-api" class="anchor mark-hover" tabindex="-1">Consuming Blob Storage from the API</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Inside the API project, add the Aspire Azure Blob Storage client integration:</p><pre id="bea800c865f8b1e1097ab47af743f6bf"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="bea800c865f8b1e1097ab47af743f6bf" data-status="bea800c865f8b1e1097ab47af743f6bf-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="bea800c865f8b1e1097ab47af743f6bf-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">Aspire.Azure.Storage.Blobs</span></div></code></pre><p>Then register the client in <code>Program.cs</code> using the <code>AddAzureBlobServiceClient</code> method, specifying the same connection name as defined in the <code>AppHost</code>.</p><pre id="70713ccd77fd49485b77788f60acc3e1"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="70713ccd77fd49485b77788f60acc3e1" data-status="70713ccd77fd49485b77788f60acc3e1-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="70713ccd77fd49485b77788f60acc3e1-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddAzureBlobServiceClient</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">connectionName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;blob-storage&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>The important part here is the connection name.
The <code>connectionName</code> in the API project must match the blob resource name from the AppHost. In this example, that name is <code>blob-storage</code>.</p><p>Aspire then handles the configuration and interaction with the blob service for you.
The API project does not need to know whether the blob service is backed by Azurite locally or by Azure Storage in another environment.
For consumers of the blob service, it&#39;s just a blob client that can be used to interact with the blob storage, regardless of the underlying implementation.</p>
		<h2 id="switching-between-local-and-azure">
		  <a href="#switching-between-local-and-azure" class="anchor mark-hover" tabindex="-1">Switching between local and Azure</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>For local development, Azurite is usually the right choice.
For deployed environments, you probably want to connect to a real Azure Storage account.</p><p>Because we&#39;re using Aspire, the configuration is handled in the <code>AppHost</code>, without any changes needed in the consumer project(s).
This makes it easy to switch between local and Azure environments because the logic is centralized in one place. You also don&#39;t have to worry about environment-specific branching in your application code.</p><p>One option is to use <code>AsExisting</code> when publishing, so the application connects to an existing Azure Storage account.
Using the <code>ExecutionContext</code> allows you to conditionally configure the storage resource based on whether you are in publish mode or not.</p><pre id="6884704203497bb50fa5769cbe87f225"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">AppHost.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="6884704203497bb50fa5769cbe87f225" data-status="6884704203497bb50fa5769cbe87f225-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="6884704203497bb50fa5769cbe87f225-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">DistributedApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">storage</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddAzureStorage</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;storage&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ExecutionContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IsPublishMode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">existingStorageName</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddParameter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;existingStorageName&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">existingStorageResourceGroup</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddParameter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;existingStorageResourceGroup&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">storage</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AsExisting</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">existingStorageName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">existingStorageResourceGroup</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">else</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">storage</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">RunAsEmulator</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">azurite</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">azurite</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithLifetime</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">ContainerLifetime</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Persistent</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithDataVolume</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">blobs</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">storage</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddBlobs</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;blob-storage&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">filesContainer</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">storage</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddBlobContainer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;app-files&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">blobContainerName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;app-files&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProject</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Projects</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Project_Api</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;project-api&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithReference</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">blobs</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WaitFor</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">filesContainer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre>
		<h2 id="using-a-connection-string-directly">
		  <a href="#using-a-connection-string-directly" class="anchor mark-hover" tabindex="-1">Using a connection string directly</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Not all packages and integrations will have built-in support for the <code>AsExisting</code> method, or similar methods to connect to existing resources.
For these cases you can provide the connection string to the resource manually.</p><p>For demo purposes, this is also a possibility for Azure Storage if you want to use the connection string directly instead of the <code>AsExisting</code> method.</p><p>Just as before, this logic is still centralized within the <code>AppHost</code>.</p><p>The difference, is that the connection string is provided directly as an environment variable to the API project when publishing, instead of configuring it through the built-in Azure Storage integration.</p><pre id="6f4c288a43c08353879d2872dea575b6"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">AppHost.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="6f4c288a43c08353879d2872dea575b6" data-status="6f4c288a43c08353879d2872dea575b6-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="6f4c288a43c08353879d2872dea575b6-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">DistributedApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">api</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProject</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Projects</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Project_Api</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;project-api&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ExecutionContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IsPublishMode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">api</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithEnvironment</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;ConnectionStrings__blob-storage&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Configuration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;ConnectionStrings:blob-storage&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">]);</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">else</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">storage</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddAzureStorage</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;storage&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">RunAsEmulator</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">azurite</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">azurite</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithLifetime</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">ContainerLifetime</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Persistent</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithDataVolume</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">blobs</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">storage</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddBlobs</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;blob-storage&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">filesContainer</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">storage</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddBlobContainer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;app-files&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant-numeric))">blobContainerName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;app-files&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">api</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithReference</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">blobs</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WaitFor</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">filesContainer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>In this version, the API still uses the same connection name: <code>blob-storage</code>.
If you&#39;re unsure about the environment variable name that is used, you can look at the Aspire dashboard, or check the documentation of the integration you&#39;re using to see how it configures the information for the consuming projects.</p>
		<h2 id="why-this-is-useful">
		  <a href="#why-this-is-useful" class="anchor mark-hover" tabindex="-1">Why this is useful</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The main benefit is not that Aspire magically removes all environment differences. You still have to make choices about how close your local environment is to production, and there are always going to be some trade-offs.</p><p>The benefit is that Aspire gives you control over those differences and makes it manageable.
Dependencies are centralized and become visible in the application model.</p><p>Your local setup becomes consistent and repeatable.
Developers have fewer manual steps and don&#39;t have to read extensive documentation files to get up and running.</p><p>Your application code doesn&#39;t get polluted with environment-specific branching logic for every integration. The application code can just consume the services it needs, and Aspire takes care of providing the right configuration for the environment it&#39;s running in.</p><p>Using the same application model, you can publish the same application to a non-local environment, but with different configurations for the dependencies. Again, without any code changes needed in the application projects.</p><p>With the many integrations available, it&#39;s easy to run a wide variety of dependencies locally, which gives you a much better feedback loop during the development process. You also don&#39;t need to know and share credentials or connection strings to connect to resources.</p><p>This makes the local environment reflect the real environment much better.
This is crucial, as many bugs are not caused by business logic alone. They appear in the interaction between those integrations.</p><div class="custom-block tip">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 14c.2-1 .7-1.7 1.5-2.5A4.8 4.8 0 0 0 18 8 6 6 0 0 0 6 8c0 1.3.5 2.5 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg></span>Tip</div>
			<p>During (integration) testing, you can also use the same approach to run tests against a production-like environment, instead of relying on mocks and stubs, you can use <a class="mark mark-hover" href="https://testcontainers.org/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Ftestcontainers.org)'>TestContainers</a> to run real instances of the dependencies your application interacts with.</p>
		</div>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>In my experience, a good local environment gives the entire team confidence, and allows them to move faster without worrying about breaking things for others or running into unexpected issues when deploying.</p><p>The local environment should be easy to start, easy to inspect and close enough to production to catch integration issues early.</p><p>Aspire helps by letting you model your application and its dependencies in one place. With its many (and growing list of) integrations, it gives you a flexible way to run local instances of your dependencies. Aspire also handles and maintains the integration with those external services, so you don&#39;t have to worry about the setup and configuration details.</p><p>Because the configuration is centralized in the <code>AppHost</code>, you can easily switch between local and production configurations without any code changes needed in your application projects.</p><p>That doesn&#39;t only make your local development less fragile, it also prevents bad deployments when new integrations are not properly configured.</p><p>This all prevents scenarios where &quot;it works on my machine&quot; is a common excuse for bugs that only appear in production, and it gives you confidence that your code works in the real environment as well as it works locally.</p><p>In this post we looked at Azure Storage as an example, but the same principles apply to any other dependency you have in your application. Aspire&#39;s flexibility and growing ecosystem of integrations make it a powerful tool for managing your local environment and ensuring that it behaves as close to production as possible.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Improve your AI coding agent with runtime context from Aspire MCP]]></title>
					<description><![CDATA[The power of giving an AI agent access to OpenTelemetry data through Aspire MCP is simple: it gives the agent better input to analyze. Instead of reasoning only from source code, the agent can inspect the actual behavior of the running system. That makes its insights more reliable, resulting in more relevant and correct suggestions.]]></description>
					<link>https://timdeschryver.dev/blog/improve-your-ai-coding-agent-with-runtime-context-from-aspire-mcp</link>
					<guid>https://timdeschryver.dev/blog/improve-your-ai-coding-agent-with-runtime-context-from-aspire-mcp</guid>
					<pubDate>Thu, 07 May 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/improve-your-ai-coding-agent-with-runtime-context-from-aspire-mcp/images/banner.webp" alt="Improve your AI coding agent with runtime context from Aspire MCP"/><p>Read <strong>Improve your AI coding agent with runtime context from Aspire MCP</strong> on <a href="https://timdeschryver.dev/blog/improve-your-ai-coding-agent-with-runtime-context-from-aspire-mcp">timdeschryver.dev</a></p>
		<h2 id="why-ai-coding-agents-need-runtime-context">
		  <a href="#why-ai-coding-agents-need-runtime-context" class="anchor mark-hover" tabindex="-1">Why AI coding agents need runtime context</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>A coding agent is as good as the context you provide it. With Aspire MCP, you can give your AI coding agents context from your running system, enabling them to make better, well-informed suggestions.</p>
		<h2 id="source-code-does-not-tell-the-whole-story">
		  <a href="#source-code-does-not-tell-the-whole-story" class="anchor mark-hover" tabindex="-1">Source code does not tell the whole story</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Modern applications are no longer simple, isolated systems. They are composed of APIs, databases, message brokers, background workers, caches, containers, and external services. Understanding how all these pieces interact can be harder than writing the code itself.</p><p>While OpenTelemetry, Aspire, and AI agents are still relatively new and are still evolving quickly, they are becoming important tools in modern software development. When I&#39;m working on projects, I can&#39;t imagine working without them anymore. Individually, they are already powerful tools, and together they reinforce each other.</p>
		<h2 id="the-runtime-context-stack">
		  <a href="#the-runtime-context-stack" class="anchor mark-hover" tabindex="-1">The runtime context stack</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2>
		<h3 id="opentelemetry-gives-you-the-signals">
		  <a href="#opentelemetry-gives-you-the-signals" class="anchor mark-hover" tabindex="-1">OpenTelemetry gives you the signals</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>OpenTelemetry provides the foundation for observability. It captures structured logs, distributed traces, and metrics, making it possible to understand what happens inside a system at runtime. Instead of guessing why a request is slow or why a service fails, OpenTelemetry gives developers the data needed to investigate the real behavior of the system.</p>
		<h3 id="aspire-application-model-and-local-orchestration">
		  <a href="#aspire-application-model-and-local-orchestration" class="anchor mark-hover" tabindex="-1">Aspire: application model and local orchestration</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Aspire orchestrates your distributed environment and gives you a structured way to model the different parts of your system using code. With Aspire, we get an isolated local development environment, with OpenTelemetry integrated from the start and support for building and deploying the application model. Logs, traces, and resource statuses are all centralized in one place, and are easily accessible in the Aspire dashboard.</p>
		<h3 id="ai-agents-better-context-means-better-suggestions">
		  <a href="#ai-agents-better-context-means-better-suggestions" class="anchor mark-hover" tabindex="-1">AI agents: better context means better suggestions</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>AI agents add another layer on top of this.
While agents already help us write, understand, and maintain code and documentation, they can do much more when they have access to the right context (information). Instead of just looking at the source code, they can also inspect the runtime behavior of the system and make more informed suggestions. This is where OpenTelemetry and Aspire come in. They already have the context that AI agents need to understand the behavior of the system, resulting in more useful assistance.</p>
		<h2 id="what-becomes-possible">
		  <a href="#what-becomes-possible" class="anchor mark-hover" tabindex="-1">What becomes possible</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>This means that instead of manually investigating logs, traces, and metrics to understand the behavior of the system, or manually debugging the application, you can ask your AI agent to do it for you.</p><p>When these three tools work together, you can ask your AI agent to analyze the runtime behavior of your system, and to provide insights and suggestions based on that. The results are more accurate and useful than if it only had access to the source code.</p><p>This is useful in different scenarios, for example:</p><ul>
<li>analyze logs to find the source of an error</li>
<li>identify the root cause of a performance issue</li>
<li>pinpoint the source of a failure in a distributed system</li>
<li>document the flow through the system</li>
<li>act as a rubber duck to explain the behavior of the system</li>
</ul>
<p>It can often go one step further: after identifying a likely issue, it can suggest and implement a fix that you can review.</p>
		<h2 id="aspire-mcp-connecting-your-agent-to-the-running-application">
		  <a href="#aspire-mcp-connecting-your-agent-to-the-running-application" class="anchor mark-hover" tabindex="-1">Aspire MCP: connecting your agent to the running application</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p><a class="mark mark-hover" href="https://aspire.dev/get-started/aspire-mcp-server/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Faspire.dev)'>Aspire MCP</a> is a local MCP server exposed by Aspire.
MCP is a protocol that lets AI tools communicate with external systems in a structured way. In our case, the external system is the Aspire application model.</p>
		<h3 id="how-to-enable-aspire-mcp">
		  <a href="#how-to-enable-aspire-mcp" class="anchor mark-hover" tabindex="-1">How to enable Aspire MCP</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>To install the Aspire MCP server, click on the MCP icon in the top right corner of the Aspire dashboard, and follow the instructions to install the MCP server. Once installed, you can enable it for your application.</p><figure>
				<img src="https://timdeschryver.dev/blog/improve-your-ai-coding-agent-with-runtime-context-from-aspire-mcp/images/0.webp" alt="" loading="lazy"/>
				<figcaption>The Aspire MCP server installation process</figcaption>
			</figure><p>You can also run the following command in your terminal to install the MCP server, follow the setup wizard, and optionally include skills and the Playwright CLI:</p><pre id="94d8f103f2cf922bc6b0034e3b8691e4"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="94d8f103f2cf922bc6b0034e3b8691e4" data-status="94d8f103f2cf922bc6b0034e3b8691e4-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="94d8f103f2cf922bc6b0034e3b8691e4-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">aspire</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">agent</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">init</span></div></code></pre>
		<h3 id="what-your-agent-can-inspect">
		  <a href="#what-your-agent-can-inspect" class="anchor mark-hover" tabindex="-1">What your agent can inspect</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>When the MCP server is enabled, your AI code agent can use the <a class="mark mark-hover" href="https://aspire.dev/get-started/aspire-mcp-server/#tools" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Faspire.dev)'>exposed tools</a>, such as reading the available resources, logs, and traces, enabling it to perform better.</p><p>Because the tools are exposed through MCP, they can be used by MCP-compatible AI tools, such as GitHub Copilot, Codex, or Claude Code.
There is no unified configuration format for every tool yet, so the setup differs per client. Luckily, the above <code>init</code> command does its best to already set up the correct integration for you based on the environment you are using. For example, if you are using GitHub Copilot, it will add the MCP server to the settings for you in Visual Studio Code.</p>
		<h2 id="example">
		  <a href="#example" class="anchor mark-hover" tabindex="-1">Example</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Imagine a front-end application that suddenly shows an empty screen.</p><p>The code looks fine at first sight. The page calls the API, the API calls another service, and the UI expects data back. Without runtime context, an AI coding agent can only inspect the source code and guess where the problem might be.</p><p>With Aspire MCP, the agent can inspect the running application instead. It can check the resource status, see that one downstream service is unhealthy, read the API logs, follow the trace from the front-end request through the backend, and connect the empty screen to the service that is actually failing.</p><p>This changes the investigation.
Instead of asking &quot;what in this code could be wrong?&quot;, the agent can ask &quot;what actually happened when this request ran?&quot;.</p>
		<h3 id="example-prompt">
		  <a href="#example-prompt" class="anchor mark-hover" tabindex="-1">Example prompt</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>An example prompt could be:</p><pre id="b18892caf66beab24615bec89396e8fc"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="6" width="16" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="11" width="12" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="16" width="16" height="2" rx="1" fill="#64748B"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="b18892caf66beab24615bec89396e8fc" data-status="b18892caf66beab24615bec89396e8fc-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="b18892caf66beab24615bec89396e8fc-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="txt " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-unknown))">The front-end application shows an empty screen when I open the orders page.</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">Please investigate this using the Aspire MCP tools before suggesting a fix.</span></div></code></pre><p>Or a longer version of the prompt to be more specific about the investigation:</p><pre id="06cc7cf033a47276baaa04ae0deae379"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="6" width="16" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="11" width="12" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="16" width="16" height="2" rx="1" fill="#64748B"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="06cc7cf033a47276baaa04ae0deae379" data-status="06cc7cf033a47276baaa04ae0deae379-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="06cc7cf033a47276baaa04ae0deae379-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="txt " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-unknown))">The front-end application shows an empty screen when I open the orders page.</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">Please investigate this using the Aspire MCP tools before suggesting a fix.</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))"></span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">Include the following checklist to structure your investigation:</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))"></span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">Check:</span></div><div class="line removal"><span style="color: hsl(var(--syntax-unknown))">the status of the Aspire resources</span></div><div class="line removal"><span style="color: hsl(var(--syntax-unknown))">the logs for the front-end, API, and downstream services</span></div><div class="line removal"><span style="color: hsl(var(--syntax-unknown))">the trace for the failing request</span></div><div class="line removal"><span style="color: hsl(var(--syntax-unknown))">any unhealthy or unavailable dependencies</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))"></span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">I want you to determine:</span></div><div class="line removal"><span style="color: hsl(var(--syntax-unknown))">which request from the front-end is failing</span></div><div class="line removal"><span style="color: hsl(var(--syntax-unknown))">whether the API receives the request</span></div><div class="line removal"><span style="color: hsl(var(--syntax-unknown))">which downstream service is involved</span></div><div class="line removal"><span style="color: hsl(var(--syntax-unknown))">whether that service is unhealthy, unavailable, or returning an error</span></div><div class="line removal"><span style="color: hsl(var(--syntax-unknown))">the most likely root cause based on the runtime data</span></div><div class="line removal"><span style="color: hsl(var(--syntax-unknown))">the safest fix or next step</span></div></code></pre><div class="custom-block tip">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 14c.2-1 .7-1.7 1.5-2.5A4.8 4.8 0 0 0 18 8 6 6 0 0 0 6 8c0 1.3.5 2.5 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg></span>Tip</div>
			<p>Install the new <a class="mark mark-hover" href="https://marketplace.visualstudio.com/items?itemName=microsoft-aspire.aspire-vscode" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fmarketplace.visualstudio.com)'>Aspire extension</a> for Visual Studio Code. The extension enhances the editor with Aspire information in the sidebar, and it also has the MCP server integration.</p>
		</div>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>OpenTelemetry provides the raw runtime logs, traces, and metrics. Aspire organizes those signals and connects them to the application model. AI agents can use that structured context to help debug issues, explain failures, identify performance problems, and suggest improvements.</p><p>That changes how we use coding agents, with the result of a significantly enhanced development experience.</p><p>Instead of manually collecting logs, copying trace output, and explaining the current state of the system in a prompt, you can ask the agent to inspect the running application directly.</p><p>The AI agent quality also sees improvements. It doesn&#39;t guess anymore, the agent uses the runtime context to analyze the system&#39;s behavior, resulting in more reliable insights and suggestions.</p><p>This allows us to spend less time on manual investigation, and more time on building reliable software that creates value.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Keep Agentic AI Simple: A Practical Workflow for Software Development]]></title>
					<description><![CDATA[You don’t need a complex setup or endless prompt tricks to get value from AI in software development. I've experienced that a few practices like an AGENTS.md file, Agent Skills, and specifications can dramatically improve the speed, quality, and consistency of agents.]]></description>
					<link>https://timdeschryver.dev/blog/keep-agentic-ai-simple-a-practical-workflow-for-software-development</link>
					<guid>https://timdeschryver.dev/blog/keep-agentic-ai-simple-a-practical-workflow-for-software-development</guid>
					<pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/keep-agentic-ai-simple-a-practical-workflow-for-software-development/images/banner.webp" alt="Keep Agentic AI Simple: A Practical Workflow for Software Development"/><p>Read <strong>Keep Agentic AI Simple: A Practical Workflow for Software Development</strong> on <a href="https://timdeschryver.dev/blog/keep-agentic-ai-simple-a-practical-workflow-for-software-development">timdeschryver.dev</a></p><p>Over the past year, I changed my perspective on AI in software development. I used to be skeptical, but after watching <a class="mark mark-hover" href="https://www.youtube.com/user/burkeholland" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.youtube.com)'>Burke Holland</a>&#39;s content on Copilot agents, I realized that I had to give it another try. From using AI as a simple code completion tool, to a tool to generate tests or simple isolated enhancements and smaller tasks, I started to see it as a system that&#39;s capable of doing much more. Fast forward to today, I&#39;m using it more and more to create new features from scratch.</p><p>Over the last month I used Agentic AI to rewrite a project entirely from scratch, and the results are amazing. The speed boost is incredible, and the quality of the code is good, not perfect, but good enough in my opinion. In this blog post, I want to share my experience and thoughts on AI in software development, and why I think it&#39;s a game-changer for software development.</p><p>No, I didn&#39;t vibe code the project, because while it&#39;s could be faster, I think this practice causes issues in the long term. Instead - and because we&#39;re currently in the classic cycling season I want to use a cycling metaphor -, I would say that I&#39;m seeing AI as a (super-)domestique, while I&#39;m the team leader.</p><blockquote><span class="icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/></svg></span><p>In road bicycle racing, a <a class="mark mark-hover" href="https://en.wikipedia.org/wiki/Domestique" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fen.wikipedia.org)'>domestique</a> is a rider who works for the benefit of their team and leader, rather than trying to win the race. In French, domestique translates as &quot;servant&quot;. The use of the term dates back to 1911, although such riders had existed before then.</p></blockquote><p>This allowed me to focus on the high-level design and architecture of the project, while I let the AI handle the implementation details and most of the work. In the end, you&#39;re still the leader who needs to cross first over the finish line, just as Wout van Aert won Paris Roubaix to raise the cobblestone. A leader who thinks about the strategy, and can communicate this clearly to his team. Just as pulling in an external dependency, we&#39;re still responsible for the overall direction and quality of the project, but we can leverage the power of AI to get there faster and more efficiently.</p>
		<h2 id="overall-architecture-and-design">
		  <a href="#overall-architecture-and-design" class="anchor mark-hover" tabindex="-1">Overall architecture and design</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The project I worked on, uses an ASP.NET API as the backend and an Angular frontend, orchestrated by Aspire.</p><p>Before letting the agents loose, I&#39;ve set up the project structure the way I wanted.</p><p>I used the same design as mentioned in <a class="mark mark-hover" href="https://timdeschryver.dev/blog/treat-your-net-minimal-api-endpoint-as-the-application-layer" data-sveltekit-reload >Treat your .NET Minimal API Endpoint as the application layer</a> using a Vertical Slice architecture. I prefer the slices approach because it separates the different uses cases of an application, which to my feeling helps the agent as well (and makes it easier to rewrite or delete code when requirements changes). For Angular, I decided to use all the Signal API features, and no external UI library. After the initial setup, I let the AI handle the implementation of the features.</p><p>The goal of this project was to act as a sandbox to experiment with different approaches and techniques, to get myself familiar with the capabilities and limitations of AI in software development.
Disclaimer: I&#39;m certainly not an AI-expert, but I wanted to share my successful experience, and I hope it can inspire you to explore AI. I also want to share some of the techniques and approaches I used, and how these helped me to get the most out of AI in software development.</p>
		<h2 id="agentsmd">
		  <a href="#agentsmd" class="anchor mark-hover" tabindex="-1">AGENTS.md</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>If you&#39;re already familiar with AI Agents, then the <a class="mark mark-hover" href="https://agents.md/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fagents.md)'>AGENTS.md</a> file shouldn&#39;t be new to you. This file is always included in the Agent&#39;s context, and it contains the instructions that the agent follows while working on the project. Because it&#39;s included for every request it&#39;s a good practice to add the general guidelines, the structure of the project, and commands the agent can use in this document. Different tools might have their own version of this file, e.g. in GitHub Copilot it&#39;s called <code>instructions.md</code>, or claude uses <code>CLAUDE.md</code>, but the idea remains the same across the tools.</p><p>Instead of different versions of this file, using the <code>AGENTS.md</code> naming is a standard, and is supported in most of the tools. However, CLAUDE code does not support this file, but it&#39;s possible to create a symlink or reference the <code>AGENTS.md</code> file using <code>@AGENTS.md</code>.
This makes it easy to switch between tools, without having to change the instructions for the agent.</p><p>You don&#39;t have to write the <code>AGENTS.md</code> file manually, you can also just let the agent generate it for you. In my case this file contains the technology stack, the commands to use (build, test, lint, etc.), the coding style and conventions, and the overall flow of the application. This way, the agent has a clear understanding of the project and its requirements, and uses this to generate code that is aligned with the overall direction of the project.</p><p>I also found that adding a database diagram in the mermaid syntax to the <code>AGENTS.md</code> file, is a great way to give the agent a better understanding of the data model of the project. In bigger application this big diagram won&#39;t be readable for humans, but it does help the agent to understand the application&#39;s model to pinpoint where it needs to be.</p><p>Because this file is critical to have good results, it&#39;s a good idea to review your <code>AGENTS.MD</code> file regularly to keep it up to date, to ensure that the instructions and guidelines are still relevant and accurate. This way, you can be confident that the agent is always working with the latest information, and that it can generate code that is aligned with the overall direction of the project.</p><p>To keep the file in a good shape, ask the agent to update the <code>AGENTS.md</code> after introducing a new concept (within the same session).
I also added recurring mistakes to this file to improve the quality of the agent.</p>
		<h2 id="skills">
		  <a href="#skills" class="anchor mark-hover" tabindex="-1">Skills</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>I do use <a class="mark mark-hover" href="https://agentskills.io/home" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fagentskills.io)'>Agent Skills</a> to extend the capabilities of the coding agent. Skills are just markdown files (it can also contain scripts) to teach your agent specific knowledge about tools and technology, or team rules that the agent needs to follow in certain situations. The agent can access these skills, and automatically pulls in the skill(s) when it detects it could be useful for a given task. For example, when I ask the agent to create a new Angular component, it loads the Angular skill, and uses it to generate the component code.</p><p>The main differentiation between skills and <code>AGENTS.md</code>, is that the agent file is always included in the context, while skills are only included when they are relevent. This is important to keep the context window manageable, in order to get the best results.</p><p>Another usage of a skill is that it&#39;s possible to invoke the skill on demand using a slash command. In this case the skills feature is the successor of slash commands or file instructions, and provides a standardized way to invoke repetitive tasks.</p><p>Many tools add the skills to the <code>./agents/skills</code> folder, however CLAUDE uses <code>./claude/skills</code>. As a workaround you can create symlinks to cover both causes without duplicating the skills.</p><p>Skills can also be picked up by different agents, this makes it easy to centralise and share the knowledge between different agents.</p><p>There are plenty of good skills available. To search for skills, I recommend using <a class="mark mark-hover" href="https://skills.sh/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fskills.sh)'>https://skills.sh</a>.
To install a skill in your project, you can use the following command. After the installation the skill is added to your project repository (or on your system), so it can be re-used by your team.</p><pre id="72eb8cb22df498a120cd53b0b04fda72"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="72eb8cb22df498a120cd53b0b04fda72" data-status="72eb8cb22df498a120cd53b0b04fda72-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="72eb8cb22df498a120cd53b0b04fda72-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">npx</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">skills</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">dotnet/skills</span></div></code></pre><p>I used skills to teach the agent the latest Angular features, using the official <a class="mark mark-hover" href="https://angular.dev/ai/agent-skills" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'>Angular skill</a>, and I also used several official <a class="mark mark-hover" href="https://github.com/dotnet/skills" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>.NET skills</a> to provide the agent the best practices (e.g. to write good and performant Entity Framework code). Besides the technical skills, I also installed the <code>pnpm</code>, <code>NuGet</code>, <code>Aspire</code>, and several documentation skills (e.g. to create mermaid diagram) to let the agent handle the project management and documentation tasks.</p><p>Using the <a class="mark mark-hover" href="https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'><code>skill-creator</code> skill</a>, it&#39;s also possible to generate your own skills, or you can also write them manually if you prefer. However using the creator skill will also include the best practices to write the skill.
I recommend creating custom skill for your team-level rules, or for areas where an agent usually goes off the track.</p><p>For my project, a must-have, was creating a design skill containing direction and feeling of the application. This skill documents the overall/brand style (colours, fonts, etc.), and the available components within the project. Without this skill, I experienced that the agent would often deviate from the general design, leading to inconsistent design elements and a bad user experience. By using the design system skill, the agent was able to generate consistent and high-quality UI code, which was aligned with the overall design direction of the project. This was especially required for the codex models, and less for the claude models.</p><p>Because most of my endpoints are implemented in the same way, I also used the existing endpoints as examples to let the agent generate an &quot;endpoint template&quot; skill. This made additional generated endpoints consistent with the existing ones.</p><p>Agent skills are a great way to improve the overall quality, from anything coding related to general tasks such as writing and updating the documentation.</p><p>That&#39;s also the reason why I&#39;m not using MCP servers. While these were heavily promoted a couple of months ago, these bloat the context window of the agents, resulting in lower quality output. There are many skills and/or CLI tools (which skills can use) that can replace tasks for an MCP server, for example for <a class="mark mark-hover" href="https://github.com/microsoft/playwright-cli" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>Playwright CLI</a>, <a class="mark mark-hover" href="https://cli.github.com/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fcli.github.com)'>GitHub CLI</a>, and many more. I&#39;ve only configured the <a class="mark mark-hover" href="https://aspire.dev/get-started/aspire-mcp-server/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Faspire.dev)'>Aspire MCP</a>, and for a brief moment a <a class="mark mark-hover" href="https://help.figma.com/hc/en-us/articles/32132100833559-Guide-to-the-Figma-MCP-server" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fhelp.figma.com)'>Figma</a> and <a class="mark mark-hover" href="https://stitch.withgoogle.com/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fstitch.withgoogle.com)'>Google Stitch</a> to try out design integrations but this wasn&#39;t a great success.</p>
		<h2 id="spec-driven-development">
		  <a href="#spec-driven-development" class="anchor mark-hover" tabindex="-1">Spec-driven development</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>While the prompt was important (see prompt-engineering), models are becoming better to interpret our requests. This is good news for me, because prompting was never my forte. To write the specifications for new features, I used spec-driven development using <a class="mark mark-hover" href="https://openspec.dev/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fopenspec.dev)'>OpenSpec</a>. I prompted what I wanted to achieve in a few sentences, and OpenSpec generated 3 files for me:</p><ul>
<li>a <code>proposal.md</code> file, where the functional analysis of the feature is described, within a few sections, a summary, a &quot;what&quot;, a why, the scope and not in scope, and a list of success criteria.</li>
<li>a <code>design.md</code> file, where the technical design of the feature is described, including the areas, API endpoints, Angular components, and models that need to be created or changed, and the implementation details.</li>
<li>a <code>tasks.md</code> file, where the step-by-step implementation plan is described, with a list of tasks that need to be done to implement the feature, and the order in which they should be done.</li>
</ul>
<p>When I was unhappy with the generated documents, I could easily change them using a second or third prompt, or make manual changes to the generated files.</p><p>I found it to be a great way to communicate the requirements and expectations to the coding agent, and it helped me to get a better understanding of the feature, the implementation, and possible caveats and drawbacks. It also helped me to keep track of the progress, and to steer that the feature is implemented according to the requirements and expectations. If you want, you can change the files OpenSpec generates, but I found that the default templates are good enough for me. An additional benefit of using OpenSpec, is that these (archived) documents are a part of the codebase, and are committed with the rest of the code.</p><p>To make smaller changes and bugfixes to existing features, I used a more traditional approach, where I just prompted the agent to make the changes directly, or to create a plan suggestion. This approach worked well without the need for a more formal spec, but I found that for larger features, the spec-driven approach was more effective and efficient. Otherwise, I found that the agent would often miss some subtle details, or that it would implement the feature in a way that was not aligned with my expectations. This meant I had to do multiple iterations to get the feature right, which was time-consuming.</p><p>In short, use spec-driven development for bigger features, while it&#39;s possible to skip this step for smaller changes or bugfixes.</p><p>Instead of OpenSpec, you can also use GitHub&#39;s <a class="mark mark-hover" href="https://github.com/github/spec-kit" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>SpecKit</a>.</p><p>This way of working also divides the agent&#39;s context in multiple steps, again resulting in a better outcome.
Another variation of spec-driven development is Research Plan Implement (RPI), which is almost the same.</p><div class="custom-block tip">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 14c.2-1 .7-1.7 1.5-2.5A4.8 4.8 0 0 0 18 8 6 6 0 0 0 6 8c0 1.3.5 2.5 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg></span>Tip</div>
			<p>The talk &quot;<a class="mark mark-hover" href="https://www.youtube.com/watch?v=rmvDxxNubIg" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.youtube.com)'>No Vibes Allowed: Solving Hard Problems in Complex Codebases</a>&quot; by Dex Horthy helped me to understand this concept better, and explains why context matters.</p>
		</div>
		<h2 id="tools-and-models">
		  <a href="#tools-and-models" class="anchor mark-hover" tabindex="-1">Tools and models</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The key to working with agents is to provide just enough context, not too little, but certainly not too much as well.</p><p>While there are a few differences in the models that I&#39;ve used (Claude and Codex), I&#39;ve found them to be equally useful.
These models evolve at a fast pace, and I think it doesn&#39;t matter as much as it used to be. Every few months a new and better model is released, so even if one model is currently better, it can shift in the other direction before you know it.
That being said, the only difference that I&#39;ve experienced, is that Claude is better in design tasks.</p><p>So far, I haven&#39;t used custom agents, mostly because I have a feeling that this will pull me into a rabbit hole, while I&#39;m pleased with the default coding agent in combination with skills.</p><p>The tools we use to interact with the models are an abstraction layer around the models, which is called the harness.
If I&#39;m well informed, this layer is becoming thinner because the power of AI lies within the models themselves.
Because of this, there isn&#39;t much difference between the tools, and they are becoming more and more identical.</p><p>This is also something that I&#39;ve experienced while trying out different tools, from GitHub Copilot, to Codex and Claude, to OpenCode.
The one harness is more independent then another, but this is just a matter of preference (and configuration).
Because the fast pace of AI, I do like and prefer <a class="mark mark-hover" href="https://opencode.ai/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fopencode.ai)'>OpenCode</a> because it provides the option to use different models while remaining in the same tool. This is similar to or <a class="mark mark-hover" href="https://github.com/copilot/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>GitHub Copilot</a>, but additionally it prevents a vendor lock-in as you can connect to different providers.</p>
		<h2 id="workflow">
		  <a href="#workflow" class="anchor mark-hover" tabindex="-1">Workflow</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Using agents has drastically changed my workflow.</p><p>Instead of writing code, and writing code, and writing code.
I now delegate this task to the coding agent, while I can give more attention to more critical aspects in the process.</p><p>I usually start with using the OpenSpec propose skill to generate a plan to implement.
When the plan is in a good shape, I ask the coding agent to implement the steps within the plan.
After the feature is complete I test the code from a functional standpoint, and I review the critical code paths in depth. To make quick progress I&#39;m not interested in the implementation details. For example, I decided to use custom UI components instead of using a component library. I treated this code as an external dependency, similar to a third-party package, and I only look at the public API, not the internals of the components.
This usually takes a few iterations, and after going back and forth, when I&#39;m happy with the results I&#39;m using the git and GitHub skills to commit the changes and open a pull request.</p><p>When the agent is busy with generating a plan or implementing the plan, I usually do one of the following: review (with or without an agent) code, start generating a new plan, updating the documentation (also using an agent), refactor code, or getting a coffee. While it worked on a big task, it can also happen that I&#39;m going for a quick run while the agent is working.</p>
		<h2 id="team-structure-and-ceremonies">
		  <a href="#team-structure-and-ceremonies" class="anchor mark-hover" tabindex="-1">Team structure and ceremonies</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>For this project, I was the sole developer, so I didn&#39;t have to worry about team structure and ceremonies. However, I&#39;m sure that communication and collaboration is more crucial than ever.</p><p>I have the feeling that what I was used to deliver in two-week sprints, is now shipped within a couple of days or even less. Therefore, having frequent check-ins and review moments, are critical to keep everyone aligned. Instead of coding for days/weeks and then having a big review at the end, it&#39;s better to have more frequent reviews. This to catch potential issues or misalignments early on, and to ensure that the project is on the right track.</p><p>That&#39;s why I also believe that the role of a Product Owner and/or Project Manager will become more important. The issue isn&#39;t that there isn&#39;t enough time and resources anymore, but that there will be an overflow of non-important features. The skill will be to focus on the areas that provide business value, and to not be tricked into all the bells and whistles that provide no-value and are quick to deliver.</p><p>I won&#39;t be surprised that pair programming could have a new meaning. Instead of having two developers working on a feature, why not having a developer and a stakeholder or key-user working on a feature together. Atleast, this is something that I&#39;m looking forward to try out.</p>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>We can&#39;t ignore that our industry is changing. To stay relevant it&#39;s important to embrace this change early on.</p><blockquote><span class="icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/></svg></span><p>As <a class="mark mark-hover" href="https://uk.linkedin.com/in/danclarkeuk" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fuk.linkedin.com)'>Dan Clarke</a> wrote in <a class="mark mark-hover" href="https://www.danclarke.com/developers-are-system-thinkers-not-code-monkeys/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.danclarke.com)'>Developers are System Thinkers, not Code Monkeys</a>:
Don&#39;t be the developer who&#39;s afraid of AI. Be the developer who treats it as the latest system to learn. Play with it. Experiment. Build things with it. The more you understand how it works, the better you&#39;ll be at leveraging it - and that&#39;s exactly what we&#39;ve always done.</p></blockquote><p>Because introducing AI can be overwhelming at first, atleast it was for me, I think it&#39;s a good idea to start simple and to not bring in all of features at once. The tools and techniques are also changing in a rapid pace, so it&#39;s a challenge to keep up with best practices, especially if it&#39;s not your main focus area. Using standardized tools and practices helps to keep everything manageable. While choosing tools, I also think it&#39;s important to think about a potential vendor lock-in.</p><p>By only using the default AI coding agent you already see some positive results, but this probably won&#39;t impress you, nor be consistent in their output. My recommendation is to start with the bare minimum, and include the files we discusses when they are required. This can be done when you notice that the agent keeps making the same mistakes.</p><p>I was impressed with the overall quality improvement with introducing the <code>AGENTS.MD</code> file, and even more so by adding Agent Skills to the project. This resulted in major speed improvements by using AI in my development workflow. To prevent countless back-and-forth&#39;s between me and the model, a good specification with clear requirements helps to boost the productivity.</p><p>To include these instructions across different tools you can use symbolic links to prevent the duplication, and the hassle to keep the files in sync. I prefer to keep the original files tools-agnostic and to stay close to standard, as a workaround create symlinks pointing to these locations to support the tools who defer from the standard. This practice makes it easier to try out new tools, or eventually switch between tools.</p><p>Because AI is evolving in a fast pace this setup needs to be continuously re-evaluated, and keep re-iterating to see what works best for you and your setup.</p><p>While this all sounds good, keep in mind that AI is not the silver bullet.
AI can help you ship faster, but only if you stay in the driver&#39;s seat.
Just as within software teams now, there will always be areas that could be (or are required to be) polished up.</p><p>I&#39;m seeing our role as software engineer becoming less technical detailed, instead our role is to provide good guardrails to let the agent do its job (and this might also become less important over the next months and years?), while still taking ownership over the product that we&#39;re building. That&#39;s why it&#39;s a good idea to start your AI journey sooner than later.</p><p>Instead of focusing on delivering code, I can now give more focus to delivering value, which was - and still is - the most important and brings me the most joy.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Containerize an ASP.NET Core BFF and Angular frontend using Aspire]]></title>
					<description><![CDATA[Using Damien Bowden's secure ASP.NET Core and Angular BFF template as a starting point, this post shows how to integrate Aspire to improve local development and prepare the application for containerized deployment.]]></description>
					<link>https://timdeschryver.dev/blog/containerize-an-aspnet-core-bff-and-angular-frontend-using-aspire</link>
					<guid>https://timdeschryver.dev/blog/containerize-an-aspnet-core-bff-and-angular-frontend-using-aspire</guid>
					<pubDate>Tue, 07 Apr 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/containerize-an-aspnet-core-bff-and-angular-frontend-using-aspire/images/banner.webp" alt="Containerize an ASP.NET Core BFF and Angular frontend using Aspire"/><p>Read <strong>Containerize an ASP.NET Core BFF and Angular frontend using Aspire</strong> on <a href="https://timdeschryver.dev/blog/containerize-an-aspnet-core-bff-and-angular-frontend-using-aspire">timdeschryver.dev</a></p><p>In this article, we use the <a class="mark mark-hover" href="https://github.com/damienbod/bff-aspnetcore-angular" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>bff-aspnetcore-angular</a> template from <a class="mark mark-hover" href="https://damienbod.com/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fdamienbod.com)'>Damien Bowden</a> as a starting point, and integrate Aspire into the project to improve the local development experience and prepare it for containerized deployment.</p><div class="custom-block info">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z"/><path d="M15 3v4a2 2 0 0 0 2 2h4"/></svg></span>Note</div>
			<p>For more information about the template, see Damien&#39;s article <a class="mark mark-hover" href="https://damienbod.com/2023/09/11/implement-a-secure-web-application-using-nx-standalone-angular-and-an-asp-net-core-server/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fdamienbod.com)'>Implement a secure web application using nx Standalone Angular and an ASP.NET Core server</a>.</p>
		</div><p>The template implements a BFF (Backend For Frontend) architecture. As Damien has said it in his article &quot;The project implements a secure web application using Angular and ASP.NET Core. The web application implements the backend for frontend security architecture (BFF) and deploys both technical stack distributions as one web application. HTTP only secure cookies are used to persist the session. Microsoft Entra ID is used as the identity provider and the token issuer.&quot;. Other security features include the use of CSP (Content Security Policy) headers and Anti-Forgery tokens.</p><p>The template and the related articles are already a great starting point for building a secure web application using ASP.NET Core and Angular. We recently used it to create a new project, and integrating Aspire improved the development experience. It also gave us a straightforward way to package the application for deployment with Docker.</p><p>If you have not looked at the template and the related articles yet, the project consists of an Angular application and an ASP.NET application.
The Angular application is served from the ASP.NET application, and the ASP.NET application acts as a BFF for the Angular application by handling authentication and API requests. While running locally, the ASP.NET application proxies requests to the Angular development server. In production, the Angular application is served as static files from the <code>wwwroot</code> folder of the ASP.NET application.</p><p>Let&#39;s go through the steps.
If you&#39;re just interested in containerizing the application, you can directly jump to the <a class="mark mark-hover" href="#dockerize-the-project" >Dockerize the project</a> section.</p>
		<h2 id="integrate-aspire-into-the-project">
		  <a href="#integrate-aspire-into-the-project" class="anchor mark-hover" tabindex="-1">Integrate Aspire into the project</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>I followed the same approach described in my <a class="mark mark-hover" href="https://timdeschryver.dev/blog/a-minimal-way-to-integrate-aspire-into-your-existing-project" data-sveltekit-reload >A minimal way to integrate Aspire into your existing project</a> article.</p><p>Within the existing solution, create a new Aspire <code>AppHost</code> project, which will be used to run both the ASP.NET Server project and the Angular project.
Then, configure the AppHost to serve the ASP.NET Server project and the Angular project.</p><p>To serve the Angular project, we can use the <code>AddJavaScriptApp</code> method from the <code>Aspire.Hosting.JavaScript</code> packages, which first needs to be installed in the AppHost project.</p><pre id="fe5c52f89a413a3f78d1f3f4b728788c"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="fe5c52f89a413a3f78d1f3f4b728788c" data-status="fe5c52f89a413a3f78d1f3f4b728788c-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="fe5c52f89a413a3f78d1f3f4b728788c-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">Aspire.Hosting.JavaScript</span></div></code></pre><p>Then, we can configure the AppHost to serve the ASP.NET and the Angular project by adding the following code to the <code>AppHost.cs</code> file.</p><pre id="010e920815808cebc2e2f160ec2dd59d"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">AppHost/AppHost.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="010e920815808cebc2e2f160ec2dd59d" data-status="010e920815808cebc2e2f160ec2dd59d-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="010e920815808cebc2e2f160ec2dd59d-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">DistributedApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">ui</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddJavaScriptApp</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;bff-angular&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;../ui&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">runScriptName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;start&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    //</span><span style="color: hsl(var(--syntax-comment))"> Automatically install npm packages when the application is run</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithNpm</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">installCommand</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;install&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">installArgs</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;--force&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">])</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    //</span><span style="color: hsl(var(--syntax-comment))"> Pass the PORT environment variable to the Angular application</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    //</span><span style="color: hsl(var(--syntax-comment))"> Which is used by the Angular development server to determine the port to run on</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithHttpEndpoint</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">env</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;PORT&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">server</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProject</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Projects</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">BffMicrosoftEntraID_Server</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;bff-server&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div></code></pre><p>Because the Angular project is served from the ASP.NET application, we need to configure the relationship between the two projects. We can do this by adding a reference to the Angular project in the ASP.NET project and configuring the ASP.NET project to serve the Angular application as static files.</p><pre id="8521932f53e697c76fbc3e51e8bd6e63"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">AppHost/AppHost.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="8521932f53e697c76fbc3e51e8bd6e63" data-status="8521932f53e697c76fbc3e51e8bd6e63-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="8521932f53e697c76fbc3e51e8bd6e63-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">DistributedApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">ui</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddJavaScriptApp</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;bff-angular&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;../ui&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">runScriptName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;start&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithNpm</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">installCommand</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;install&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">installArgs</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;--force&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">])</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithHttpEndpoint</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">env</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;PORT&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">server</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProject</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Projects</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">BffMicrosoftEntraID_Server</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;bff-server&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithReference</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">ui</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WaitFor</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">ui</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithChildRelationship</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">ui</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div></code></pre><p>Creating this relation between the two projects and create an <code>services__bff-angular__http__0</code> environment variable that contains the URL of the Angular application, which can be used in the ASP.NET application to configure the static file serving. In the current version this URL is hardcoded.</p>
		<h3 id="servicedefaults">
		  <a href="#servicedefaults" class="anchor mark-hover" tabindex="-1">ServiceDefaults</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>You can also create a new <code>ServiceDefaults</code> project to configure the standard middleware for all services in one place, for example, to add OpenTelemetry support.
After creating the project, add a reference to the <code>ServiceDefaults</code> project in the <code>Server</code> project and call the <code>AddServiceDefaults</code> method in the <code>Program.cs</code> file to apply the default configuration to the server project.</p><pre id="a261fb40c4b5401cc5d8a5e5d064c502"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Server/Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="a261fb40c4b5401cc5d8a5e5d064c502" data-status="a261fb40c4b5401cc5d8a5e5d064c502-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="a261fb40c4b5401cc5d8a5e5d064c502-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddServiceDefaults</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre>
		<h2 id="server-changes">
		  <a href="#server-changes" class="anchor mark-hover" tabindex="-1">Server changes</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>To reroute the Angular routes to the Angular development server, the template uses <a class="mark mark-hover" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/yarp/yarp-overview?WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'>YARP (Yet Another Reverse Proxy)</a>. To know the URL of the Angular development server, the template uses a hardcoded URL in the <code>appsettings.Development.config</code> file. This is not ideal, as it can lead to issues when running the project in different environments. By using the environment variable created by the AppHost, we can make the configuration more flexible and avoid hardcoding URLs. The <code>bff-angular</code> is the name of the Angular project given in the <code>AppHost.cs</code> file.</p><pre id="331bb7476029bfd2671a8a977a6ef175"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="6" width="16" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="11" width="12" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="16" width="16" height="2" rx="1" fill="#64748B"/>
</svg>
 <span class="file-name">"appsettings.Development.config"</span> <button type="button" class="copy-code icon align-text-top" data-ref="331bb7476029bfd2671a8a977a6ef175" data-status="331bb7476029bfd2671a8a977a6ef175-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="331bb7476029bfd2671a8a977a6ef175-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="diff " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">{</span></div><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-invalid))">  &quot;UiDevServerUrl&quot;: &quot;https://localhost:4201&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  &quot;ReverseProxy&quot;: {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    &quot;Routes&quot;: {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        ...</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    },</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    &quot;Clusters&quot;: {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      &quot;cluster1&quot;: {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        &quot;HttpClient&quot;: {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          &quot;SslProtocols&quot;: [&quot;Tls12&quot;]</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        },</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        &quot;Destinations&quot;: {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          &quot;cluster1/destination1&quot;: {</span></div><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-invalid))">            &quot;Address&quot;: &quot;https://localhost:4201/&quot;</span></div><div class="line addition"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-entity-name-section))">            &quot;Address&quot;: &quot;https+http://bff-angular&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          }</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        }</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      }</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    }</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">}</span></div></code></pre><p>This doesn&#39;t work out of the box, because YARP can&#39;t resolve the <code>bff-angular</code> URL. To make it work, we need to configure YARP to use the environment variable created by the AppHost for the Angular development server URL. This can be done by installing the <code>Microsoft.Extensions.ServiceDiscovery.Yarp</code> package in the Server project and configuring YARP to use the environment variable for the Angular development server URL.</p><pre id="2024303c790f15f0edd98ef2e3b63135"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="2024303c790f15f0edd98ef2e3b63135" data-status="2024303c790f15f0edd98ef2e3b63135-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="2024303c790f15f0edd98ef2e3b63135-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">Microsoft.Extensions.ServiceDiscovery.Yarp</span></div></code></pre><p>After the installation, configure YARP to discover destinations using the <code>AddServiceDiscoveryDestinationResolver()</code> extension method.</p><pre id="f3645ff14e0b54868ea98953ae142378"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Server/Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="f3645ff14e0b54868ea98953ae142378" data-status="f3645ff14e0b54868ea98953ae142378-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="f3645ff14e0b54868ea98953ae142378-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddServiceDefaults</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">..</span><span style="color: hsl(var(--syntax-meta-tag))">.</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">services</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddReverseProxy</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">LoadFromConfig</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetSection</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;ReverseProxy&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">))</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddServiceDiscoveryDestinationResolver</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>To render the Angular application, also update the <code>_Host.cshtml</code> file to load the index.html file from the Angular development server in development. Again, this uses the environment variable instead of the <code>UiDevServerUrl</code> configuration value that was previously hard coded.</p><pre id="1be7851310403ea900e85a49339bc551"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">server/Pages/_Host.cshtml</span> <button type="button" class="copy-code icon align-text-top" data-ref="1be7851310403ea900e85a49339bc551" data-status="1be7851310403ea900e85a49339bc551-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="1be7851310403ea900e85a49339bc551-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">@page</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;/&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">@namespace</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">BlazorBffAzureAD</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Pages</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">@using</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">System</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Net</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">@using</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">NetEscapades</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AspNetCore</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">SecurityHeaders</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">@addTagHelper</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">*</span><span style="color: hsl(var(--syntax-meta-tag))">, </span><span style="color: hsl(var(--syntax-meta-tag))">Microsoft</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AspNetCore</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Mvc</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">TagHelpers</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">@addTagHelper</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">*</span><span style="color: hsl(var(--syntax-meta-tag))">, </span><span style="color: hsl(var(--syntax-meta-tag))">NetEscapades</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AspNetCore</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">SecurityHeaders</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">TagHelpers</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">@inject</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">IHostEnvironment</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">hostEnvironment</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">@inject</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">IConfiguration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">config</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">@inject</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Microsoft</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AspNetCore</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Antiforgery</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IAntiforgery</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">antiForgery</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">@</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">Layout</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">null</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">source</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">hostEnvironment</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IsDevelopment</span><span style="color: hsl(var(--syntax-invalid-deprecated))">())</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">httpClient</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">HttpClient</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">source</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">httpClient</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetStringAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">$&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">config</span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;BFF_ANGULAR_HTTPS&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">]</span><span style="color: hsl(var(--syntax-meta-property-value-css))"> </span><span style="color: hsl(var(--syntax-constant))">??</span><span style="color: hsl(var(--syntax-meta-property-value-css))"> </span><span style="color: hsl(var(--syntax-meta-tag))">config</span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;BFF_ANGULAR_HTTP&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">]}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">/index.html&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">else</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">source</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">System</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IO</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">File</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">ReadAllText</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">$&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">System</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IO</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Directory</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetCurrentDirectory</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()}{</span><span style="color: hsl(var(--syntax-meta-property-value-css))">@&quot;/wwwroot/index.html&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">nonce</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">HttpContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetNonce</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    //</span><span style="color: hsl(var(--syntax-comment))"> The nonce is passed to the client through the HTML to avoid sync issues between tabs</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">source</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">source</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Replace</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;**PLACEHOLDER_NONCE_SERVER**&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">nonce</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">hostEnvironment</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IsDevelopment</span><span style="color: hsl(var(--syntax-invalid-deprecated))">())</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">        //</span><span style="color: hsl(var(--syntax-comment))"> do nothing in development, Angular &gt; 18.1.0 adds the nonce automatically</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">viteScriptToUpdate</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;&quot;&lt;script type=&quot;module&quot; src=&quot;/@vite/client&quot;&gt;&lt;/script&gt;&quot;&quot;&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">source</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">source</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Replace</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">viteScriptToUpdate</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">$&quot;&quot;&quot;&lt;script type=&quot;module&quot; src=&quot;/@vite/client&quot; nonce=&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">nonce</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&gt;&lt;/script&gt;&quot;&quot;&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    //</span><span style="color: hsl(var(--syntax-comment))"> link rel=&quot;stylesheet&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">nonceLinkStyle</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">$&quot;&lt;link nonce=</span><span style="color: hsl(var(--syntax-constant))">\&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">nonce</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))">\&quot;</span><span style="color: hsl(var(--syntax-meta-property-value-css))"> rel=</span><span style="color: hsl(var(--syntax-constant))">\&quot;</span><span style="color: hsl(var(--syntax-meta-property-value-css))">stylesheet&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">source</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">source</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Replace</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&lt;link rel=</span><span style="color: hsl(var(--syntax-constant))">\&quot;</span><span style="color: hsl(var(--syntax-meta-property-value-css))">stylesheet&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">nonceLinkStyle</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    //</span><span style="color: hsl(var(--syntax-comment))"> link rel=&quot;modulepreload&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">nonceLinkModulePreload</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">$&quot;&lt;link nonce=</span><span style="color: hsl(var(--syntax-constant))">\&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">nonce</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))">\&quot;</span><span style="color: hsl(var(--syntax-meta-property-value-css))"> rel=</span><span style="color: hsl(var(--syntax-constant))">\&quot;</span><span style="color: hsl(var(--syntax-meta-property-value-css))">modulepreload&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">source</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">source</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Replace</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&lt;link rel=</span><span style="color: hsl(var(--syntax-constant))">\&quot;</span><span style="color: hsl(var(--syntax-meta-property-value-css))">modulepreload&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">nonceLinkModulePreload</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">xsrf</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">antiForgery</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetAndStoreTokens</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">HttpContext</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">requestToken</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">xsrf</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">RequestToken</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    //</span><span style="color: hsl(var(--syntax-comment))"> The XSRF-Tokens are passed to the client through cookies, since we always want the most up-to-date cookies across all tabs</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">Response</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Cookies</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Append</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;XSRF-RequestToken&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">requestToken</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">??</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CookieOptions</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">HttpOnly</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">IsEssential</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Secure</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">SameSite</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">SameSiteMode</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Strict</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">@Html</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Raw</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">source</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div></code></pre>
		<h2 id="angular-changes">
		  <a href="#angular-changes" class="anchor mark-hover" tabindex="-1">Angular changes</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>In the original template, the Angular application was built into the wwwroot folder so it&#39;s automatically included when the ASP.NET application is deployed.
Because this isn&#39;t the default behavior when building an Angular application and it&#39;s also not very visible (added to the <code>angular.json</code> configuration), I prefer to use Aspire&#39;s infrastructure.</p><p>Therefore I removed the output path configuration from the <code>angular.json</code> file.
Now when the application is built, it reverts back to the default behavior to add the output build into the <code>dist</code> folder of the Angular project.</p><pre id="78ed6b83e6b4962e5a1051b38502e584"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="6" width="16" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="11" width="12" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="16" width="16" height="2" rx="1" fill="#64748B"/>
</svg>
 <span class="file-name">angular.json</span> <button type="button" class="copy-code icon align-text-top" data-ref="78ed6b83e6b4962e5a1051b38502e584" data-status="78ed6b83e6b4962e5a1051b38502e584-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="78ed6b83e6b4962e5a1051b38502e584-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="diff " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  &quot;$schema&quot;: &quot;./node_modules/@angular/cli/lib/config/schema.json&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  &quot;version&quot;: 1,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  &quot;newProjectRoot&quot;: &quot;projects&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  &quot;projects&quot;: {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    &quot;ui&quot;: {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      &quot;projectType&quot;: &quot;application&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      &quot;schematics&quot;: {},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      &quot;root&quot;: &quot;&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      &quot;sourceRoot&quot;: &quot;src&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      &quot;prefix&quot;: &quot;app&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      &quot;architect&quot;: {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        &quot;build&quot;: {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          &quot;builder&quot;: &quot;@angular/build:application&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          &quot;options&quot;: {</span></div><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-invalid))">            &quot;outputPath&quot;: {</span></div><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-invalid))">              &quot;base&quot;: &quot;../server/wwwroot&quot;,</span></div><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-invalid))">              &quot;browser&quot;: &quot;&quot;</span></div><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-invalid))">            },</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            &quot;browser&quot;: &quot;src/main.ts&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            &quot;polyfills&quot;: [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              &quot;zone.js&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              &quot;@angular/localize/init&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            ],</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            &quot;tsConfig&quot;: &quot;tsconfig.app.json&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            &quot;assets&quot;: [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                &quot;glob&quot;: &quot;**/*&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                &quot;input&quot;: &quot;public&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              }</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            ],</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            &quot;styles&quot;: [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              &quot;node_modules/bootstrap/dist/css/bootstrap.min.css&quot;,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              &quot;src/styles.css&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            ]</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          },</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        }</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    }</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  }</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">}</span></div></code></pre>
		<h2 id="dockerize-the-project">
		  <a href="#dockerize-the-project" class="anchor mark-hover" tabindex="-1">Dockerize the project</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>So far, all these changes improve the development experience, but they also make it easy to containerize the application with Docker. By using Aspire&#39;s infrastructure, we can build and run the application without having to manually wire the configuration of the individual projects.</p><p>First, we need to add the <code>Aspire.Hosting.Docker</code> package to the AppHost project, which adds the container publishing capabilities we need.</p><pre id="58bf8157e71c7443482d0d6516a9e89d"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="58bf8157e71c7443482d0d6516a9e89d" data-status="58bf8157e71c7443482d0d6516a9e89d-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="58bf8157e71c7443482d0d6516a9e89d-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">Aspire.Hosting.Docker</span></div></code></pre><p>Next, replace the AppHost contents with the following code, which configures the application for container publishing.</p><pre id="a561b4bed1a72e5354d5e47a245b883b"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">AppHost/AppHost.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="a561b4bed1a72e5354d5e47a245b883b" data-status="a561b4bed1a72e5354d5e47a245b883b-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="a561b4bed1a72e5354d5e47a245b883b-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">DistributedApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">compose</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddDockerComposeEnvironment</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;compose&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithDashboard</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">ui</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddJavaScriptApp</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;bff-angular&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;../ui&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">runScriptName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;start&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithNpm</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">installCommand</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;install&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">installArgs</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;--force&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">])</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithHttpEndpoint</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">env</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;PORT&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithAnnotation</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ContainerFilesSourceAnnotation</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">SourcePath</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;/app/dist/ui/browser&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">server</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProject</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Projects</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">BffMicrosoftEntraID_Server</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;bff-server&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithExternalHttpEndpoints</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">PublishWithContainerFiles</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">ui</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;./wwwroot&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">PublishAsDockerComposeService</span><span style="color: hsl(var(--syntax-invalid-deprecated))">((</span><span style="color: hsl(var(--syntax-constant-numeric))">_</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">_</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> In publish mode the Angular app is embedded in the server Docker image.</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> The dev-time frontend process is only needed when running locally.</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ExecutionContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IsPublishMode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">server</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithReference</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">ui</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WaitFor</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">ui</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithChildRelationship</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">ui</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>Here we see some new concepts, let&#39;s go through them:</p><p>The <code>AddDockerComposeEnvironment</code> method creates a new Docker Compose setup that can run multiple services together. When the image is built, we do not want to include the Aspire Dashboard, so we disable it with the <code>WithDashboard(false)</code> method.</p><p>For the Angular project, we add a <code>ContainerFilesSourceAnnotation</code>, which tells Aspire where to find the Angular build output as a source for published container files.</p><p>For the ASP.NET project, we use the <code>PublishWithContainerFiles</code> method to include output of the Angular application within the ASP.NET application&#39;s image. This copies the content (the <code>dist</code> folder) from the Angular application to the <code>wwwroot</code> folder during publishing.</p><p>The <code>WithExternalHttpEndpoints</code> method configures the container to expose the HTTP endpoints of the ASP.NET application outside the container. Because the Angular application is served from the ASP.NET application, its content is also exposed through the same HTTP endpoint. If we don&#39;t do this, the ASP.NET application will be running in the container, but we won&#39;t be able to access it from outside the container.</p><p>Finally, we use the <code>PublishAsDockerComposeService</code> method to add this project as a service in the Docker Compose setup. This allows us to run the application with Docker Compose.</p><p>Resulting in the following <code>docker-compose.yaml</code> file being generated after running <code>aspire publish</code>:</p><pre id="b9c61c82ef3e631060da4ed29fae9d33"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.53267 5.86364H9.28835L11.9418 10.875H12.0554L14.7088 5.86364H17.4645L13.2202 13.3864V17.5H10.777V13.3864L6.53267 5.86364Z" fill="#A78BFA"/>
</svg>
 <span class="file-name">docker-compose.yaml</span> <button type="button" class="copy-code icon align-text-top" data-ref="b9c61c82ef3e631060da4ed29fae9d33" data-status="b9c61c82ef3e631060da4ed29fae9d33-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="b9c61c82ef3e631060da4ed29fae9d33-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="yaml " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">services</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-entity-name-section))">bff-server</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-entity-name-section))">image</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'${BFF_SERVER_IMAGE}'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-entity-name-section))">environment</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'in_memory'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">ASPNETCORE_FORWARDEDHEADERS_ENABLED</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'true'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-entity-name-section))">HTTP_PORTS</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'${BFF_SERVER_PORT}'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-entity-name-section))">ports</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'${BFF_SERVER_PORT}'</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-entity-name-section))">networks</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">-</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'aspire'</span></div><div class="line "><span style="color: hsl(var(--syntax-entity-name-section))">networks</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-entity-name-section))">aspire</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-entity-name-section))">driver</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'bridge'</span></div></code></pre><p>The <code>publish</code> commands also generated the <code>.env</code> file containing the variables used in the <code>docker-compose.yaml</code> file, in this case, the <code>BFF_SERVER_IMAGE</code> and <code>BFF_SERVER_PORT</code> variables.</p><pre id="0ae86d9e7488666a72ffff8d286b3eb4"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.53267 5.86364H9.28835L11.9418 10.875H12.0554L14.7088 5.86364H17.4645L13.2202 13.3864V17.5H10.777V13.3864L6.53267 5.86364Z" fill="#A78BFA"/>
</svg>
 <span class="file-name">.env</span> <button type="button" class="copy-code icon align-text-top" data-ref="0ae86d9e7488666a72ffff8d286b3eb4" data-status="0ae86d9e7488666a72ffff8d286b3eb4-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="0ae86d9e7488666a72ffff8d286b3eb4-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="yaml " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">#</span><span style="color: hsl(var(--syntax-comment))"> Container image name for bff-server</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">BFF_SERVER_IMAGE=</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">#</span><span style="color: hsl(var(--syntax-comment))"> Default container port for bff-server</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">BFF_SERVER_PORT=</span></div></code></pre><p>Note that we don&#39;t we only create a reference between the Angular and ASP.NET project in development, but not in publish mode. This is because in publish mode, the Angular application is built and included in the ASP.NET application&#39;s image. This is required, otherwise you will receive an error during publishing that the Angular project can&#39;t be found, because it&#39;s not included in the publish context. Howerver, at the time of writing this post it does seem that this issue is resolved in the latest version of Aspire (13.2.1), so you might be able to include the reference in publish mode as well.</p>
		<h2 id="next-steps">
		  <a href="#next-steps" class="anchor mark-hover" tabindex="-1">Next steps</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>If you want to customize the Docker image further, you can set <a class="mark mark-hover" href="https://aspire.dev/integrations/compute/docker/#configure-image-pull-policy" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Faspire.dev)'>image tags</a>, set up a <a class="mark mark-hover" href="https://aspire.dev/integrations/compute/docker/#configure-a-container-registry" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Faspire.dev)'>container registry</a>, and <a class="mark mark-hover" href="https://aspire.dev/integrations/compute/docker/#github-actions-workflow-example" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Faspire.dev)'>integrate it with GitHub Actions</a> for automated builds and deployments.</p>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>By integrating the template with Aspire first, we improve the local development experience without changing the core BFF architecture. Service discovery removes hardcoded frontend URLs, and the same setup makes it much easier to package the application for Docker-based deployment. The result is a cleaner development workflow today and a more predictable path to containerized hosting tomorrow.</p><p>Using the best practices already set out by Damien&#39;s template, this gives us a solid foundation for building secure web applications with ASP.NET Core and Angular, while also leveraging Aspire&#39;s capabilities for orchestration and containerization.</p><p>You can find the full project on <a class="mark mark-hover" href="https://github.com/timdeschryver/aspire-bff-aspnetcore-angular" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>GitHub</a>, or inspect the <a class="mark mark-hover" href="https://github.com/damienbod/bff-aspnetcore-angular/compare/main...timdeschryver:aspire-bff-aspnetcore-angular:main" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>commit</a> that turns Damien&#39;s original template into an Aspire project with Docker support.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Introducing Angular Testing Library Zoneless]]></title>
					<description><![CDATA[To keep up with modern Angular, there's a new zoneless subpackage that helps you write component tests.]]></description>
					<link>https://timdeschryver.dev/blog/introducing-angular-testing-library-zoneless</link>
					<guid>https://timdeschryver.dev/blog/introducing-angular-testing-library-zoneless</guid>
					<pubDate>Thu, 19 Mar 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/introducing-angular-testing-library-zoneless/images/banner.webp" alt="Introducing Angular Testing Library Zoneless"/><p>Read <strong>Introducing Angular Testing Library Zoneless</strong> on <a href="https://timdeschryver.dev/blog/introducing-angular-testing-library-zoneless">timdeschryver.dev</a></p><p><a class="mark mark-hover" href="https://testing-library.com/angular" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Ftesting-library.com)'>Angular Testing Library</a> now introduces a new <code>zoneless</code> subpackage, a trimmed-down version of the original Angular Testing Library. This is avalable from version 19.2.1 onwards.</p>
		<h2 id="why">
		  <a href="#why" class="anchor mark-hover" tabindex="-1">Why</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The reason for the new <code>@testing-library/angular/zoneless</code> subpackage is that, over time, Angular&#39;s testing capabilities have improved, and some of the features provided by the original Angular Testing Library have become less necessary. It focuses on a more streamlined API that leverages Angular&#39;s built-in testing features.</p>
		<h2 id="disabled-automatic-change-detection">
		  <a href="#disabled-automatic-change-detection" class="anchor mark-hover" tabindex="-1">Disabled Automatic Change Detection</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The biggest change is that Testing Library&#39;s API is no longer monkey patched, such as for user events and the <code>waitFor</code> and <code>findBy</code> methods, to invoke Angular&#39;s change detection using <code>detectChanges</code>. This is something you won&#39;t notice by looking at the API, but it&#39;s a major internal change. This change is necessary to more closely reflect how the component behaves under real circumstances. It shouldn&#39;t have a big impact on existing test cases.</p>
		<h2 id="compact-api-surface">
		  <a href="#compact-api-surface" class="anchor mark-hover" tabindex="-1">Compact API Surface</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>This subpackage also includes breaking changes compared with what you&#39;re currently used to.
We think these breaking changes have a positive effect during rendering.
For example, the <code>componentProperties</code>, <code>componentInputs</code>, <code>componentOutputs</code>, and <code>inputs</code> options have been removed in favor of a single <code>bindings</code> option that makes use of <a class="mark mark-hover" href="https://angular.dev/guide/testing/components-scenarios#binding-signals-to-inputs" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'>binding syntax</a> to test <code>input</code>, <code>output</code>, and <code>model</code> bindings. Previously, there were subtle differences, which made it unclear when to use one over the other.</p><pre id="d302f2985ca510f36007b0709f590b79"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">example.spec.ts</span> <button type="button" class="copy-code icon align-text-top" data-ref="d302f2985ca510f36007b0709f590b79" data-status="d302f2985ca510f36007b0709f590b79-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="d302f2985ca510f36007b0709f590b79-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">render</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@testing-library/angular/zoneless'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-meta-tag))">userEvent</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@testing-library/user-event'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">test</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'renders and interacts with a component'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">user</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">userEvent</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">setup</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">render</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">CustomerDetails</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    bindings</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span><span style="color: hsl(var(--syntax-constant-numeric))">inputBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'customerName'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Alice'</span><span style="color: hsl(var(--syntax-meta-tag))">)]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> You can still configure providers</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    providers</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> []</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">loadButton</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">getByRole</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'button'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-property-value-css))"> /load customer/</span><span style="color: hsl(var(--syntax-constant))">i</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">user</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">click</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">loadButton</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">expect</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">getByText</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Hello Alice'</span><span style="color: hsl(var(--syntax-meta-tag))">))</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">toBeInTheDocument</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre>
		<h3 id="configuretestbed-for-more-control">
		  <a href="#configuretestbed-for-more-control" class="anchor mark-hover" tabindex="-1"><code>configureTestBed</code> for more control</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>To make the library less dependent on Angular&#39;s APIs, some configuration options that were rarely used have been removed.
Instead, it&#39;s now possible to manually configure the testing environment using Angular&#39;s <code>TestBed</code> API, which provides more flexibility and control over your specific testing setup.</p><p>To configure the <code>TestBed</code>, use the <code>configureTestBed</code> method. For example, you can configure the router or defer behavior.</p><pre id="30197af9d04e672a7cda85854610a899"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">example.spec.ts</span> <button type="button" class="copy-code icon align-text-top" data-ref="30197af9d04e672a7cda85854610a899" data-status="30197af9d04e672a7cda85854610a899-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="30197af9d04e672a7cda85854610a899-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">render</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@testing-library/angular/zoneless'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-meta-tag))">userEvent</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@testing-library/user-event'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">test</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'renders and interacts with a component'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">user</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">userEvent</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">setup</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">render</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">CustomerDetails</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">configureTestBed</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">testBed</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-meta-tag))">testBed</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">configureTestingModule</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        deferBlockBehavior</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">DeferBlockBehavior</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Manual</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre>
		<h3 id="vitest-compatibility">
		  <a href="#vitest-compatibility" class="anchor mark-hover" tabindex="-1">Vitest Compatibility</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>A nice side effect of the <code>zoneless</code> subpackage is that it&#39;s better compatible with <a class="mark mark-hover" href="https://vitest.dev/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fvitest.dev)'>Vitest</a> (browser mode).</p>
		<h3 id="future-improvements">
		  <a href="#future-improvements" class="anchor mark-hover" tabindex="-1">Future Improvements</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Of course, we will re-evaluate the API in the future and add more features if needed. For now, the <code>zoneless</code> package provides the bare minimum needed to test components, while offering a more focused and efficient testing experience for Angular developers.</p>
		<h2 id="call-to-action">
		  <a href="#call-to-action" class="anchor mark-hover" tabindex="-1">Call to Action</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The <code>zoneless</code> subpackage is available as a separate package, so you can choose to use it without affecting your existing tests. We encourage you to try it out and share your feedback. Feel free to <a class="mark mark-hover" href="https://github.com/testing-library/angular-testing-library/issues/new" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>open issues</a> or contribute to the project if you have suggestions for improvements or new features. Your feedback helps shape the future of Angular Testing Library.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Angular Signal Forms Keeps Improving (v21.2)]]></title>
					<description><![CDATA[Taking a quick glance at the new features shipped in Angular Signal Forms. We'll cover provideSignalFormsConfig, focusBoundControl, the Submission API, and the formRoot directive, and why they are useful.]]></description>
					<link>https://timdeschryver.dev/blog/angular-signal-forms-keeps-improving</link>
					<guid>https://timdeschryver.dev/blog/angular-signal-forms-keeps-improving</guid>
					<pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/angular-signal-forms-keeps-improving/images/banner.webp" alt="Angular Signal Forms Keeps Improving (v21.2)"/><p>Read <strong>Angular Signal Forms Keeps Improving (v21.2)</strong> on <a href="https://timdeschryver.dev/blog/angular-signal-forms-keeps-improving">timdeschryver.dev</a></p><p>In the previous blog post <a class="mark mark-hover" href="https://timdeschryver.dev/blog/refactoring-a-form-to-a-signal-form" data-sveltekit-reload >&quot;Refactoring a form to a Signal Form&quot;</a>, we refactored an existing Angular form to use the new Signal Forms feature. At the time, Signal Forms were still experimental, and we mentioned that there were some limitations and areas for improvement.</p><p>Since then, the Angular team has continued to improve Signal Forms. In this blog post, we will take a look at some of the improvements that were added in recent versions of Signal Forms (at the time of writing v21.2), including application-wide CSS classes, programmatic focus management, and a more structured submission flow.</p>
		<h2 id="providesignalformsconfig">
		  <a href="#providesignalformsconfig" class="anchor mark-hover" tabindex="-1">provideSignalFormsConfig</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>To start with, there&#39;s a new method <a class="mark mark-hover" href="https://angular.dev/api/forms/signals/provideSignalFormsConfig" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'><code>provideSignalFormsConfig</code></a> to configure the Signal Forms in an application.</p><p>Currently, this method allows us to set CSS classes through the <code>classes</code> option, which are applied to form controls based on their state.
This can be used to easily apply consistent styling across form controls, e.g. to re-apply the <code>ng-*</code> classes that were present in the previous version of Angular forms.</p><p>The <code>classes</code> option accepts an object where the keys are the CSS class names and the values are functions that receive the <code>FormField</code> to determine when to apply the class based on the form control&#39;s state. Of course, this is not limited to re-applying the <code>ng-*</code> classes. You can define any custom classes that you want to apply based on the form control&#39;s state.</p><pre id="d01c5959b6a17d18732534663b197dee"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">"app.config.ts"</span> <button type="button" class="copy-code icon align-text-top" data-ref="d01c5959b6a17d18732534663b197dee" data-status="d01c5959b6a17d18732534663b197dee-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="d01c5959b6a17d18732534663b197dee-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">ApplicationConfig</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/core'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">provideSignalFormsConfig</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/forms/signals'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">appConfig</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ApplicationConfig</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  providers</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">provideSignalFormsConfig</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      classes</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'ng-dirty'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">({</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">state</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">state</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">dirty</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'ng-touched'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">({</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">state</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">state</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">touched</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'ng-valid'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">({</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">state</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">state</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">valid</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'ng-invalid'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">({</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">state</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">state</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">invalid</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'custom-class'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">({</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">state</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">state</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">touched</span><span style="color: hsl(var(--syntax-meta-tag))">() </span><span style="color: hsl(var(--syntax-constant))">&amp;&amp;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">state</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">invalid</span><span style="color: hsl(var(--syntax-meta-tag))">()</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">...</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  ]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div></code></pre>
		<h3 id="ng-status-classes">
		  <a href="#ng-status-classes" class="anchor mark-hover" tabindex="-1">NG_STATUS_CLASSES</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>If you want to re-apply all the <code>ng-*</code> classes, Angular provides a predefined set of classes called <a class="mark mark-hover" href="https://angular.dev/api/forms/signals/compat/NG_STATUS_CLASSES" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'><code>NG_STATUS_CLASSES</code></a> that you can use with <code>provideSignalFormsConfig</code> to have the same behavior as the previous version of Angular forms. This is a great option for those who want to migrate to Signal Forms without losing the styling.</p><pre id="331a23946f8da506f57cc8d03ac18b1c"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">"app.config.ts"</span> <button type="button" class="copy-code icon align-text-top" data-ref="331a23946f8da506f57cc8d03ac18b1c" data-status="331a23946f8da506f57cc8d03ac18b1c-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="331a23946f8da506f57cc8d03ac18b1c-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">ApplicationConfig</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/core'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">provideSignalFormsConfig</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/forms/signals'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">NG_STATUS_CLASSES</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/forms/signals/compat'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">appConfig</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ApplicationConfig</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  providers</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">provideSignalFormsConfig</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      classes</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">NG_STATUS_CLASSES</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  ]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div></code></pre>
		<h2 id="focusboundcontrol">
		  <a href="#focusboundcontrol" class="anchor mark-hover" tabindex="-1">focusBoundControl</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Another improvement is the addition of the <a class="mark mark-hover" href="https://angular.dev/api/forms/signals/FieldState#focusBoundControl" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'><code>focusBoundControl</code> method</a> on the <code>FormField</code> instance.</p><p>With <code>focusBoundControl</code>, we can programmatically focus the first form control (based on the DOM order) within a field tree.
This only works when the form control is already rendered in the DOM. If there are no elements to be focused, the method does nothing.</p><pre id="745782ad44a91173856c79aee26523dd"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="745782ad44a91173856c79aee26523dd" data-status="745782ad44a91173856c79aee26523dd-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="745782ad44a91173856c79aee26523dd-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">focusBoundControl</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>We use this method to focus the first form control when a dialog is opened.</p><p>It also opens up new possibilities for improving the user experience, especially when it comes to form validation. For example, we can use this method to focus the first invalid form control.</p><p>To do that, we can use the <code>errorSummary</code> on the form instance to get a summary of all the errors in the form. Then, we can call <code>focusBoundControl</code> on the first field tree to focus the first invalid form control.</p><pre id="4b03ee99d4187ddc735afbfc9af03ab6"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="4b03ee99d4187ddc735afbfc9af03ab6" data-status="4b03ee99d4187ddc735afbfc9af03ab6-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="4b03ee99d4187ddc735afbfc9af03ab6-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">errorSummary</span><span style="color: hsl(var(--syntax-meta-tag))">()[</span><span style="color: hsl(var(--syntax-constant-numeric))">0</span><span style="color: hsl(var(--syntax-meta-tag))">]</span><span style="color: hsl(var(--syntax-constant))">?.</span><span style="color: hsl(var(--syntax-constant-numeric))">fieldTree</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">focusBoundControl</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>Please keep in mind that focusing an input automatically can cause accessibility issues, so ensure it enhances the user experience rather than detracting from it.</p>
		<h2 id="submission-api">
		  <a href="#submission-api" class="anchor mark-hover" tabindex="-1">Submission API</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The third improvement that we will discuss in this blog post is the new Submission API. The Submission API provides a more structured way to handle form submissions, including both valid and invalid form states.</p><p>You can implement an asynchronous submission action using the <code>submission</code> option in the <code>form</code> function. The <code>submission</code> option accepts an object with an <code>action</code> property that is called when a <strong>valid</strong> form is submitted.</p><p>The <code>action</code> receives the form instance as an argument, which you can use to access the form value and perform any necessary operations, such as making an API call to submit the form data. This action marks all the fields in the form as touched, so you don&#39;t have to worry about that in your implementation. The <code>action</code> returns a <code>TreeValidationResult</code>, which can be used to set validation errors on the form based on the response from the server.</p><pre id="9054aeb43d0fd196819436c21252245c"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="9054aeb43d0fd196819436c21252245c" data-status="9054aeb43d0fd196819436c21252245c-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="9054aeb43d0fd196819436c21252245c-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">signal</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">FormSchema</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))">...</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">path</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Validation comes here</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> 👇 New submission API</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    submission</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant-numeric))">action</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">form</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">value</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">try</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">service</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">submit</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">value</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">set</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))">...</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">reset</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">undefined</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">catch</span><span style="color: hsl(var(--syntax-meta-tag))"> (</span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-meta-tag))">) </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            kind</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'server'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            message</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">instanceof</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">HttpErrorResponse</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">title</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Something went wrong. Please try again.'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">satisfies</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ValidationError</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">WithOptionalFieldTree</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>The <code>submission</code> option also accepts an <code>onInvalid</code> property that is called when an <strong>invalid</strong> form is submitted. This can be used to handle any specific logic when the form is invalid. This is great in combination with the <code>focusBoundControl</code> method that we discussed earlier, as we can focus the first invalid form control when the form is submitted in an invalid state. <code>onInvalid</code> receives the form instance as an argument, and does not return anything.</p><pre id="4216e0a2af838fb25ec89b1b928a09ec"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="4216e0a2af838fb25ec89b1b928a09ec" data-status="4216e0a2af838fb25ec89b1b928a09ec-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="4216e0a2af838fb25ec89b1b928a09ec-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">signal</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">FormSchema</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))">...</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">path</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Validation comes here</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> 👇 New submission API</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    submission</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant-numeric))">action</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">form</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">value</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">try</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">service</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">submit</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">value</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">set</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))">...</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">reset</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">undefined</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">catch</span><span style="color: hsl(var(--syntax-meta-tag))"> (</span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-meta-tag))">) </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            kind</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'server'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            message</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">instanceof</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">HttpErrorResponse</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">title</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Something went wrong. Please try again.'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">satisfies</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ValidationError</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">WithOptionalFieldTree</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant-numeric))">onInvalid</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">form</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">errorSummary</span><span style="color: hsl(var(--syntax-meta-tag))">()[</span><span style="color: hsl(var(--syntax-constant-numeric))">0</span><span style="color: hsl(var(--syntax-meta-tag))">]</span><span style="color: hsl(var(--syntax-constant))">?.</span><span style="color: hsl(var(--syntax-constant-numeric))">fieldTree</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">focusBoundControl</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>But how do we trigger the submission action?
Instead of implementing a <code>submit</code> event handler, we now set the (new) <code>formRoot</code> directive on the form element to bind the form instance to the form in the template. This automatically handles the form submission (for example, when the user clicks a submit button or presses enter in an input field), and triggers the appropriate submission action based on the form&#39;s validity.</p>
		<h3 id="formroot-directive">
		  <a href="#formroot-directive" class="anchor mark-hover" tabindex="-1">formRoot directive</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>The <code>formRoot</code> directive also adds the <code>novalidate</code> attribute to the form element, which disables the browser&#39;s default validation.</p><pre id="d48599095c6426b436fbcbb8904b3f83"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">"form.component.html"</span> <button type="button" class="copy-code icon align-text-top" data-ref="d48599095c6426b436fbcbb8904b3f83" data-status="d48599095c6426b436fbcbb8904b3f83-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="d48599095c6426b436fbcbb8904b3f83-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="html " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">form</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[formRoot]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;form&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">form</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div></code></pre><p>The directive needs to be imported from the <code>@angular/forms/signals</code> package in the component&#39;s imports array.</p><pre id="8c515d855f2ab0e14b05cf043613b59a"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">"form.component.ts"</span> <button type="button" class="copy-code icon align-text-top" data-ref="8c515d855f2ab0e14b05cf043613b59a" data-status="8c515d855f2ab0e14b05cf043613b59a-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="8c515d855f2ab0e14b05cf043613b59a-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Component</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/core'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">FormRoot</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/forms/signals'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">@</span><span style="color: hsl(var(--syntax-constant-numeric))">Component</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  selector</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'app-form'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">  imports</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span><span style="color: hsl(var(--syntax-meta-tag))">FormRoot</span><span style="color: hsl(var(--syntax-meta-tag))">]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  templateUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'./form.component.html'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">FormComponent</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant))">...</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>These improvements make Signal Forms feel more complete and practical to use in real applications. With <code>provideSignalFormsConfig</code>, <code>focusBoundControl</code>, the Submission API, and the <code>formRoot</code> directive, several rough edges from the earlier versions have already been smoothed out. Signal Forms is clearly evolving quickly, and these additions make it an even more compelling option if you want to build forms with Angular&#39;s signal-based APIs.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[A minimal way to integrate Aspire into your existing project]]></title>
					<description><![CDATA[Did you know that you don't need to rebuild your solution to adopt Aspire? Instead, start small and improve over time. Let's take a look at how to integrate Aspire into your existing project with minimal changes, allowing for a gradual adoption of Aspire's features.]]></description>
					<link>https://timdeschryver.dev/blog/a-minimal-way-to-integrate-aspire-into-your-existing-project</link>
					<guid>https://timdeschryver.dev/blog/a-minimal-way-to-integrate-aspire-into-your-existing-project</guid>
					<pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/a-minimal-way-to-integrate-aspire-into-your-existing-project/images/banner.webp" alt="A minimal way to integrate Aspire into your existing project"/><p>Read <strong>A minimal way to integrate Aspire into your existing project</strong> on <a href="https://timdeschryver.dev/blog/a-minimal-way-to-integrate-aspire-into-your-existing-project">timdeschryver.dev</a></p><p>We adopted Aspire in our (.NET 8) codebase without touching our existing architecture, and it worked.</p><p><a class="mark mark-hover" href="https://aspire.dev/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Faspire.dev)'>Aspire</a> isn&#39;t just for new projects. Existing projects can also benefit from Aspire&#39;s features. If you have an existing project and want to integrate Aspire, you don&#39;t have to go all-in from the start. In this post, we&#39;ll take a look at a minimal integration approach that lets you gradually adopt Aspire without having a big impact on the rest of your existing codebase.</p><p>Aspire has many great features, but you don&#39;t have to use all of them at once. Recently, our team started integrating Aspire into our existing project (on .NET 8) by only creating an Aspire <code>AppHost</code> project to run all of our projects with a single command. Doing this also allowed us to migrate from <a class="mark mark-hover" href="https://github.com/dotnet/tye" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>Project Tye</a> without making significant changes to our codebase and build processes right away. This gave us a chance to understand how Aspire works and how it can improve our development process.</p>
		<h2 id="what-minimal-means-in-this-post">
		  <a href="#what-minimal-means-in-this-post" class="anchor mark-hover" tabindex="-1">What &quot;minimal&quot; means in this post</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>This post focuses on the smallest useful integration:</p><ul>
<li>Add an Aspire <code>AppHost</code> project.</li>
<li>Register existing .NET and Angular projects in the <code>AppHost</code>.</li>
<li>Run everything from one place.</li>
</ul>
<p>It does <strong>not</strong> yet cover service defaults, containers, secrets migration, or deployment.</p>
		<h2 id="creating-the-apphost-project">
		  <a href="#creating-the-apphost-project" class="anchor mark-hover" tabindex="-1">Creating the AppHost project</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>To get started with the bare minimum, add a new project using the <code>Aspire AppHost</code> template to your solution.</p><p>You can do this in your IDE by right-clicking on your solution, selecting &quot;Add&quot; &gt; &quot;New Project&quot;, and then searching for &quot;Aspire AppHost&quot;. Follow the prompts to create the project. The project will be created with an empty <code>AppHost</code> file that serves as the entry point for your application.</p><pre id="aa3792a2f5fc7afc3eeb02c86eb09af8"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">AppHost.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="aa3792a2f5fc7afc3eeb02c86eb09af8" data-status="aa3792a2f5fc7afc3eeb02c86eb09af8-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="aa3792a2f5fc7afc3eeb02c86eb09af8-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">DistributedApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>Alternatively, you can use the <a class="mark mark-hover" href="https://aspire.dev/get-started/install-cli/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Faspire.dev)'>Aspire CLI</a> to create the project with the following command. The command runs a wizard that guides you through creating a new Aspire AppHost project and asks which projects you want to include.</p><p>Sadly, the command didn&#39;t work for our project at the time of writing (I suspect this is due to our current SDK/tooling combination on .NET 8), but it&#39;s still worth trying in your environment.</p><pre id="373278ac791e16490bbabb453a0a1107"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="373278ac791e16490bbabb453a0a1107" data-status="373278ac791e16490bbabb453a0a1107-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="373278ac791e16490bbabb453a0a1107-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">aspire</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">init</span></div></code></pre>
		<h2 id="adding-net-projects-to-the-apphost">
		  <a href="#adding-net-projects-to-the-apphost" class="anchor mark-hover" tabindex="-1">Adding .NET projects to the AppHost</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Our project has multiple ASP.NET projects, and we wanted to run all of them with a single command. To do this, we started registering them in the <code>AppHost</code> file using the <code>builder.AddProject&lt;T&gt;()</code> method (you also need to add references to these projects in the <code>AppHost</code> project file).</p><pre id="dfa50f2e1d76785bb0f4816311e4a72e"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">AppHost.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="dfa50f2e1d76785bb0f4816311e4a72e" data-status="dfa50f2e1d76785bb0f4816311e4a72e-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="dfa50f2e1d76785bb0f4816311e4a72e-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">DistributedApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">projectA</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProject</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Projects</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ProjectA</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;project-a&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">projectB</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProject</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Projects</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ProjectB</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;project-b&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><div class="custom-block tip">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 14c.2-1 .7-1.7 1.5-2.5A4.8 4.8 0 0 0 18 8 6 6 0 0 0 6 8c0 1.3.5 2.5 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg></span>Tip</div>
			<p>If you have a lot of projects, adding all projects manually can be boring.
In our experience, we found that Copilot is perfectly fine to wire up the AppHost file for us.</p>
		</div>
		<h2 id="adding-angular-projects-to-the-apphost">
		  <a href="#adding-angular-projects-to-the-apphost" class="anchor mark-hover" tabindex="-1">Adding Angular projects to the AppHost</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Besides our ASP.NET projects, we also have Angular projects in our solution. We wanted to run those from the same place, so we added them to the AppHost too.
Before adding an Angular project to the AppHost, add the <code>Aspire.Hosting.JavaScript</code> package to the AppHost project.</p><pre id="fe5c52f89a413a3f78d1f3f4b728788c"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="fe5c52f89a413a3f78d1f3f4b728788c" data-status="fe5c52f89a413a3f78d1f3f4b728788c-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="fe5c52f89a413a3f78d1f3f4b728788c-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">Aspire.Hosting.JavaScript</span></div></code></pre><p>The next step is to register the Angular project(s) in the <code>AppHost</code> file using the <code>builder.AddJavaScriptApp()</code> method.</p><pre id="12119dfc670e2e6b11eb9072f24e8980"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">AppHost.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="12119dfc670e2e6b11eb9072f24e8980" data-status="12119dfc670e2e6b11eb9072f24e8980-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="12119dfc670e2e6b11eb9072f24e8980-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">DistributedApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">projectA</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProject</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Projects</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ProjectA</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;project-a&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">projectB</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProject</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Projects</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ProjectB</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;project-b&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">frontend</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddJavaScriptApp</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;frontend&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;../AngularProject&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithNpm</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">installCommand</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;ci&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;https://localhost:4200&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre>
		<h2 id="running-the-apphost">
		  <a href="#running-the-apphost" class="anchor mark-hover" tabindex="-1">Running the AppHost</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Now that we have our projects registered in the AppHost, we can run the AppHost - just as any other project - to see the results. When you run the AppHost, it will start all of the registered projects and provide you with a dashboard.</p><figure>
				<img src="https://timdeschryver.dev/blog/a-minimal-way-to-integrate-aspire-into-your-existing-project/images/aspire-dashboard.webp" alt="" loading="lazy"/>
				<figcaption>The Aspire dashboard showing all registered projects are up and running.</figcaption>
			</figure><p>Here, we can also see that JavaScript projects include an installer resource that installs npm dependencies for those projects, so you don&#39;t have to worry about that anymore. You can opt out of this behavior by setting the <code>install</code> property to <code>false</code> in the <code>WithNpm()</code> method, for example: <code>WithNpm(installCommand: &quot;ci&quot;, install: false)</code>.</p><p>Within the dashboard, the only working feature for us right now is the application logs.</p>
		<h2 id="next-steps">
		  <a href="#next-steps" class="anchor mark-hover" tabindex="-1">Next steps</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>This is just the beginning of what you can do with Aspire.
With this minimal integration in place, you can start exploring more Aspire features as you become more comfortable with it, such as:</p><ul>
<li>Enabling <strong>Aspire&#39;s distributed tracing</strong>: With Aspire&#39;s distributed tracing, you can get insights into how your different projects are interacting with each other, and identify any performance bottlenecks or issues in your application. This can be done using a <code>ServiceDefaults</code> project and by configuring existing projects to use <code>ServiceDefaults</code>.</li>
<li>Integrating <strong>Docker containers</strong>: To create an isolated environment for your application, it&#39;s possible to integrate Docker containers into your AppHost. This allows you to run your application in a consistent environment across different machines and platforms. For example, you can add a SQL or Redis container to your AppHost.</li>
<li>Migrating to <strong>Aspire&#39;s secrets management</strong>: With Aspire&#39;s secrets management, you can securely store and manage sensitive information such as database connection strings, API keys, and other secrets. This migration will move existing secrets and appsettings to the AppHost.</li>
<li>Thinking about <strong>Release management</strong>: It&#39;s also possible to use Aspire to generate artifacts to host your application. For example, you can generate a docker compose file containing the entire project (with dependencies), or you can also set up an entire environment within Azure with all the necessary resources to host your application.</li>
</ul>

		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Aspire is a great tool to orchestrate and manage your applications.
In this post, we&#39;ve seen that you don&#39;t have to go all-in with Aspire from the start and that you can gradually introduce it into your existing project.</p><p>My team started with a simple Aspire integration that focuses on one practical outcome: running all projects with a single command from the AppHost. For us, this was a quick and easy way to introduce Aspire.</p><p>Through the Aspire dashboard we can already access logs from all our projects in one place. There&#39;s also a <a class="mark mark-hover" href="https://aspire.dev/get-started/configure-mcp/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Faspire.dev)'>MCP Server</a>, which will become smarter and better over time, when we enable more features.</p><p>From here, we can explore additional Aspire features as we get more comfortable with it and unlock more value without refactoring our existing codebase right away.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Improve the quality of your Angular component snapshots with Vitest]]></title>
					<description><![CDATA[Snapshot testing Angular components in Vitest can be noisy. Learn how to clean up your snapshots with Angular-aware serializers to get reviewable DOM snapshots. This practice can also be applied to projects migrating from Jest to Vitest, ensuring consistent snapshots across test runners.]]></description>
					<link>https://timdeschryver.dev/blog/improve-the-quality-of-your-angular-component-snapshots-with-vitest</link>
					<guid>https://timdeschryver.dev/blog/improve-the-quality-of-your-angular-component-snapshots-with-vitest</guid>
					<pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/improve-the-quality-of-your-angular-component-snapshots-with-vitest/images/banner.webp" alt="Improve the quality of your Angular component snapshots with Vitest"/><p>Read <strong>Improve the quality of your Angular component snapshots with Vitest</strong> on <a href="https://timdeschryver.dev/blog/improve-the-quality-of-your-angular-component-snapshots-with-vitest">timdeschryver.dev</a></p><p>To test an Angular component, I sometimes use a snapshot test to validate the DOM. This can be a quick way to verify a component with few interactions renders correctly. Snapshot testing was already possible with Jest, but if you are migrating your test suite to Vitest, you will notice a couple of differences.</p><p>In this post I want to point out a small change you can make to improve your snapshots.
If you were using Jest, this improvement is also helpful because you do not have to change your existing tests.</p>
		<h2 id="your-first-snapshot-test">
		  <a href="#your-first-snapshot-test" class="anchor mark-hover" tabindex="-1">Your first snapshot test</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><div class="custom-block info">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z"/><path d="M15 3v4a2 2 0 0 0 2 2h4"/></svg></span>Note</div>
			<p>Snapshot testing is a testing technique where you take a snapshot of the output of a component or function and compare it to a previously stored snapshot. Running the test will generate a snapshot if it doesn&#39;t exist, or compare the output to the existing snapshot if it does exist. If the output has changed, the test will fail, indicating that there has been a change in the component&#39;s output. This can be useful for catching unintended changes in the component&#39;s behavior or appearance.</p>
		</div><p>For a small demonstration, let&#39;s take a look at the following snapshot test, which renders the <code>App</code> component and verifies the fixture matches the snapshot.</p><pre id="198a5f01cc0d9246abb3e39fb5dadff3"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">app.spec.ts</span> <button type="button" class="copy-code icon align-text-top" data-ref="198a5f01cc0d9246abb3e39fb5dadff3" data-status="198a5f01cc0d9246abb3e39fb5dadff3-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="198a5f01cc0d9246abb3e39fb5dadff3-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">render</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@testing-library/angular/zoneless'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">App</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'./app'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">it</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'creates the app'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">fixture</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">render</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">App</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">expect</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">fixture</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">toMatchSnapshot</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>Running the above test for the first time can take a long time, and may even time out.
At the end, the test should generate a snapshot, and when you look at the output, it explains why it was so slow.
In this example, the snapshot contains about 803,000 lines of output, because it includes the entire component fixture.
Here&#39;s a small snippet of the generated snapshot:</p><pre id="101bd8700911f7e908216f275c02b1bb"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="6" width="16" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="11" width="12" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="16" width="16" height="2" rx="1" fill="#64748B"/>
</svg>
 <span class="file-name">__snapshots__/app.spec.ts.snap</span> <button type="button" class="copy-code icon align-text-top" data-ref="101bd8700911f7e908216f275c02b1bb" data-status="101bd8700911f7e908216f275c02b1bb-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="101bd8700911f7e908216f275c02b1bb-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="txt " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-unknown))">// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))"></span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">exports[`App &gt; should create the app 1`] = `</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">ComponentFixture {</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">  &quot;_appRef&quot;: ApplicationRef {</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">    &quot;_destroyListeners&quot;: [],</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">    &quot;_destroyed&quot;: false,</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">    &quot;_injector&quot;: R3Injector {</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">      &quot;_destroyed&quot;: false,</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">      &quot;_ngOnDestroyHooks&quot;: Set {</span></div><div class="line "><span style="color: hsl(var(--syntax-unknown))">...</span></div></code></pre><p>As you can see, this is not very useful.
To make the test valuable, we can pass the HTML element to the snapshot instead of the component fixture.</p><pre id="7267ec29370f0bf79c36226959775496"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">app.spec.ts</span> <button type="button" class="copy-code icon align-text-top" data-ref="7267ec29370f0bf79c36226959775496" data-status="7267ec29370f0bf79c36226959775496-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="7267ec29370f0bf79c36226959775496-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">render</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@testing-library/angular/zoneless'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">App</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'./app'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">it</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'creates the app'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">fixture</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">container</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">render</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">App</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">expect</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">fixture</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">nativeElement</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">toMatchSnapshot</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> or</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">expect</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">container</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">toMatchSnapshot</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>Running the tests again now results in a better snapshot.
In the new snapshot you can see the DOM content of the <code>App</code> component.
For example, we can now clearly see that the component renders 3 card components.</p><pre id="103716416dec032a538d8e2ac893e33c"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">__snapshots__/app.spec.ts.snap</span> <button type="button" class="copy-code icon align-text-top" data-ref="103716416dec032a538d8e2ac893e33c" data-status="103716416dec032a538d8e2ac893e33c-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="103716416dec032a538d8e2ac893e33c-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="html " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`creates the app 1`] = `</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_nghost-a-c4254472376</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">id</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;root0&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">ng-version</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;21.1.3&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c4254472376</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_nghost-a-c2695974021</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c2695974021</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-shadow&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c2695974021</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-divider&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid))">/</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c4254472376</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;!--</span><span style="color: hsl(var(--syntax-comment))">container</span><span style="color: hsl(var(--syntax-invalid-deprecated))">--&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        Card Title 1</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c4254472376</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Card details</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c4254472376</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_nghost-a-c2695974021</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c2695974021</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-shadow&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c2695974021</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-divider&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid))">/</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c4254472376</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;!--</span><span style="color: hsl(var(--syntax-comment))">container</span><span style="color: hsl(var(--syntax-invalid-deprecated))">--&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        Card Title 2</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c4254472376</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Card details</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c4254472376</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_nghost-a-c2695974021</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c2695974021</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-shadow&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c2695974021</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-divider&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid))">/</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c4254472376</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;!--</span><span style="color: hsl(var(--syntax-comment))">container</span><span style="color: hsl(var(--syntax-invalid-deprecated))">--&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        Card Title 3</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_ngcontent-a-c4254472376</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Card details</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;!--</span><span style="color: hsl(var(--syntax-comment))">container</span><span style="color: hsl(var(--syntax-invalid-deprecated))">--&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">`;</span></div></code></pre>
		<h2 id="improve-the-snapshot-quality-using-snapshot-serializers">
		  <a href="#improve-the-snapshot-quality-using-snapshot-serializers" class="anchor mark-hover" tabindex="-1">Improve the snapshot quality using snapshot serializers</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>While this is an improvement, and you might find this is already good enough, I think the snapshot contains too much noise.
When you pay close attention (to be honest, you cannot miss it), you notice additions injected by Angular, such as <code>_ng</code> attributes and <code>&lt;!--container--&gt;</code> comments. These make the snapshot harder to review and the snapshot is more likely to change, for example after an Angular upgrade.</p><p>If you are already familiar with Jest snapshot testing, this extra noise should be something you do not see in Jest snapshots.
This is not because of the Jest test runner, but because the <a class="mark mark-hover" href="https://thymikee.github.io/jest-preset-angular/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fthymikee.github.io)'><code>jest-preset-angular</code> library</a> makes use of custom snapshot serializers to prettify the output. To resolve the Vitest snapshot pollution, we&#39;re also required to use custom serializers in Vitest.</p><p>To configure Vitest, first create a <code>vitest.config.ts</code> file in the <code>src</code> folder of your Angular project.
Next, reference this file in <code>angular.json</code> as the <code>runnerConfig</code> option to extend (or modify) the default Angular testing behavior.
With this change, the Angular test runner picks up the custom configuration to configure the Vitest test runner.</p><pre id="b642ce1ffb602e77bcfbccc03eed0bd5"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.77778 6.66667C4.77778 5.19391 5.97169 4 7.44444 4C7.93536 4 8.33333 4.39797 8.33333 4.88889C8.33333 5.3798 7.93536 5.77778 7.44444 5.77778C6.95353 5.77778 6.55556 6.17575 6.55556 6.66667V10.1675C6.55556 10.8682 6.28251 11.5173 5.82622 12C6.28251 12.4827 6.55556 13.1318 6.55556 13.8325V17.3333C6.55556 17.8243 6.95353 18.2222 7.44444 18.2222C7.93536 18.2222 8.33333 18.6202 8.33333 19.1111C8.33333 19.602 7.93536 20 7.44444 20C5.97169 20 4.77778 18.8061 4.77778 17.3333V13.8325C4.77778 13.4246 4.50018 13.0691 4.10448 12.9701L3.6733 12.8623C3.2776 12.7635 3 12.4079 3 12C3 11.5921 3.2776 11.2365 3.6733 11.1377L4.10448 11.0299C4.50018 10.9309 4.77778 10.5754 4.77778 10.1675V6.66667ZM19 6.66667C19 5.19391 17.8061 4 16.3333 4C15.8424 4 15.4444 4.39797 15.4444 4.88889C15.4444 5.3798 15.8424 5.77778 16.3333 5.77778C16.8243 5.77778 17.2222 6.17575 17.2222 6.66667V10.1675C17.2222 10.8682 17.4953 11.5173 17.9516 12C17.4953 12.4827 17.2222 13.1318 17.2222 13.8325V17.3333C17.2222 17.8243 16.8243 18.2222 16.3333 18.2222C15.8424 18.2222 15.4444 18.6202 15.4444 19.1111C15.4444 19.602 15.8424 20 16.3333 20C17.8061 20 19 18.8061 19 17.3333V13.8325C19 13.4246 19.2776 13.0691 19.6733 12.9701L20.1044 12.8623C20.5002 12.7635 20.7778 12.4079 20.7778 12C20.7778 11.5921 20.5002 11.2365 20.1044 11.1377L19.6733 11.0299C19.2776 10.9309 19 10.5754 19 10.1675V6.66667Z" fill="#8B5CF6"/>
</svg>
 <span class="file-name">angular.json</span> <button type="button" class="copy-code icon align-text-top" data-ref="b642ce1ffb602e77bcfbccc03eed0bd5" data-status="b642ce1ffb602e77bcfbccc03eed0bd5-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="b642ce1ffb602e77bcfbccc03eed0bd5-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="json dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">$schema</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;./node_modules/@angular/cli/lib/config/schema.json&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">version</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">1</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">newProjectRoot</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;projects&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">projects</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">sandbox</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">test</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">builder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;@angular/build:unit-test&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">options</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">runnerConfig</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;src/vitest.config.ts&quot;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>Now, back to the <code>vitest.config.ts</code> file, where we need to set the snapshot serializer(s) used by Vitest.
Luckily, it&#39;s possible to reuse the already existing serializers of <code>jest-preset-angular</code>.</p><p><code>jest-preset-angular</code> provides 3 different serializers, each focusing on one aspect of the snapshot:</p><ul>
<li><code>html-comment</code>: removes the <code>&lt;!--container--&gt;</code> comments from the snapshot</li>
<li><code>ng-snapshot</code>: allows you to snapshot test a component fixture instead of a DOM element (just as the very first snapshot example at the top of the page)</li>
<li><code>no-ng-attributes</code>: removes the <code>_ng</code> attributes from the snapshot</li>
</ul>
<pre id="ccb1cb7ccf26b1e1313bf70ac52894b1"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">vitest.config.ts</span> <button type="button" class="copy-code icon align-text-top" data-ref="ccb1cb7ccf26b1e1313bf70ac52894b1" data-status="ccb1cb7ccf26b1e1313bf70ac52894b1-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="ccb1cb7ccf26b1e1313bf70ac52894b1-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">defineConfig</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'vitest/config'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> More info at https://angular.dev/guide/testing#advanced-vitest-configuration</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export default </span><span style="color: hsl(var(--syntax-constant-numeric))">defineConfig</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">  test</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">    snapshotSerializers</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">[</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">      </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'jest-preset-angular/build/serializers/html-comment'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">      </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'jest-preset-angular/build/serializers/ng-snapshot'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">      </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'jest-preset-angular/build/serializers/no-ng-attributes'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">    </span><span style="color: hsl(var(--syntax-meta-tag))">]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>If you do not have <code>jest-preset-angular</code> installed, first add it using the following command:</p><pre id="0723a64a6e87e7b020070e1b30fc16b8"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="0723a64a6e87e7b020070e1b30fc16b8" data-status="0723a64a6e87e7b020070e1b30fc16b8-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="0723a64a6e87e7b020070e1b30fc16b8-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">npm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">install</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">jest-preset-angular</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">--save-dev</span></div></code></pre><p>After this change, you can revert the earlier test change and simplify the test by passing the component instance to the snapshot.
Behind the scenes, the <code>ng-snapshot</code> serializer converts the instance to the HTML structure.</p><pre id="8347137b840c89c9042f524d53f3ce03"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="6" width="16" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="11" width="12" height="2" rx="1" fill="#64748B"/>
<rect x="4" y="16" width="16" height="2" rx="1" fill="#64748B"/>
</svg>
 <span class="file-name">app.spec.ts</span> <button type="button" class="copy-code icon align-text-top" data-ref="8347137b840c89c9042f524d53f3ce03" data-status="8347137b840c89c9042f524d53f3ce03-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="8347137b840c89c9042f524d53f3ce03-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="diff " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">import { render } from '@testing-library/angular/zoneless';</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">import { App } from './app';</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">it('creates the app', async () =&gt; {</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  const {fixture} = await render(App);</span></div><div class="line removal"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-invalid))">  expect(fixture.nativeElement).toMatchSnapshot();</span></div><div class="line addition"><span style="color: hsl(var(--syntax-invalid-deprecated))"></span><span style="color: hsl(var(--syntax-entity-name-section))">  expect(fixture).toMatchSnapshot();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">});</span></div></code></pre><div class="custom-block tip">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 14c.2-1 .7-1.7 1.5-2.5A4.8 4.8 0 0 0 18 8 6 6 0 0 0 6 8c0 1.3.5 2.5 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg></span>Tip</div>
			<p>While you&#39;re in the process of migrating your Jest test suite to use Vitest, this also means that your snapshots will almost be identical between Jest and Vitest.
One of the differences you&#39;ll still notice is how the snapshots are named, which is slightly different.
But, the snapshot content should be the same.</p>
		</div><p>After running the test again and approving the new snapshot, this version becomes much more compact,
allowing you and your team to focus on the important output.
This effect can clearly be seen in the new version of the snapshot:</p><pre id="9ae7c0a9a81f2a2fa94354ede7e02508"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">__snapshots__/app.spec.ts.snap</span> <button type="button" class="copy-code icon align-text-top" data-ref="9ae7c0a9a81f2a2fa94354ede7e02508" data-status="9ae7c0a9a81f2a2fa94354ede7e02508-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="9ae7c0a9a81f2a2fa94354ede7e02508-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="html " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`creates the app 1`] = `</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-shadow&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-divider&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid))">/</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> Card Title 1 </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Card details</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-shadow&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-divider&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid))">/</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> Card Title 2 </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Card details</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-shadow&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">class</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;card-divider&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid))">/</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> Card Title 3 </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card-title</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Card details</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">app-card</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">`;</span></div></code></pre>
		<h2 id="analogjs">
		  <a href="#analogjs" class="anchor mark-hover" tabindex="-1">Analog.js</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>There is also a Vitest serializer in the <code>@analogjs/vitest</code> library, but it does not have as rich a feature set as the serializers from <code>jest-preset-angular</code>. The serializer provided by <code>@analogjs</code> is similar to <code>ng-snapshot</code>, which is the least interesting one in my opinion. This <a class="mark mark-hover" href="https://github.com/analogjs/analog/issues/2065" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>could change in the future (feel free to help with this GitHub issue)</a>, because for now it can feel strange to still import <code>jest-preset-angular</code> and this leads to conflicting peer dependencies.
To start using the <code>@analogjs</code> serializer, take a look at the <a class="mark mark-hover" href="https://analogjs.org/docs/features/testing/vitest" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fanalogjs.org)'>documentation</a>.</p>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Snapshot tests can be a simple way to validate component output, but the default snapshot format is noisy. By adding Angular-aware serializers, you get snapshots that are stable and easier to review. This is useful for new projects, but also especially for projects that are migrating from Jest to Vitest because the snapshot versions remain consistent.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Changing how ASP.NET generates OpenAPI schema names]]></title>
					<description><![CDATA[Preventing any naming conflicts in the OpenAPI documentation by ensuring that each schema name is uniquely identified by its full name.]]></description>
					<link>https://timdeschryver.dev/blog/changing-how-aspnet-generates-openapi-schema-names</link>
					<guid>https://timdeschryver.dev/blog/changing-how-aspnet-generates-openapi-schema-names</guid>
					<pubDate>Thu, 29 Jan 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/changing-how-aspnet-generates-openapi-schema-names/images/banner.webp" alt="Changing how ASP.NET generates OpenAPI schema names"/><p>Read <strong>Changing how ASP.NET generates OpenAPI schema names</strong> on <a href="https://timdeschryver.dev/blog/changing-how-aspnet-generates-openapi-schema-names">timdeschryver.dev</a></p><p>By default, <a class="mark mark-hover" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/overview?WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'>ASP.NET generates OpenAPI</a> schema names based on the names of your models. However, there are scenarios where you might want to customize these names to better fit your API design. In my case, to avoid naming conflicts. In this post we&#39;ll explore how to change the way ASP.NET generates OpenAPI schema names.</p>
		<h2 id="setting-up-openapi-in-aspnet">
		  <a href="#setting-up-openapi-in-aspnet" class="anchor mark-hover" tabindex="-1">Setting up OpenAPI in ASP.NET</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>To enable OpenAPI in your ASP.NET application, you add the OpenAPI services and middleware using the <code>AddOpenApi</code> and <code>MapOpenApi</code> methods respectively:</p><pre id="f3b37fecafbda92eb2014da7273814df"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="f3b37fecafbda92eb2014da7273814df" data-status="f3b37fecafbda92eb2014da7273814df-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="f3b37fecafbda92eb2014da7273814df-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddOpenApi</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Environment</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IsDevelopment</span><span style="color: hsl(var(--syntax-invalid-deprecated))">())</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapOpenApi</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>This generates an OpenAPI document that describes your API endpoints and models, which can be accessed at <code>/openapi/v1.json</code>.</p>
		<h2 id="naming-conflicts-in-openapi-schemas">
		  <a href="#naming-conflicts-in-openapi-schemas" class="anchor mark-hover" tabindex="-1">Naming Conflicts in OpenAPI Schemas</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The problem I recently faced involved duplicate model names across different namespaces. For example, I had two classes named <code>ResponseData</code> in separate namespaces: <code>Project.ModuleA.ResponseData</code> and <code>Project.ModuleB.ResponseData</code>. When generating the OpenAPI documentation, both classes were represented as <code>ResponseData</code>, leading to conflicts.</p><p>This creates ambiguity in the API documentation and breaks code generation tools that rely on unique schema names.</p><p>As an example, take a look the following endpoints, which both return a <code>ResponseData</code> object from different namespaces:</p><pre id="b653b1cf5d56ba95b6da248e123b70a9"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="b653b1cf5d56ba95b6da248e123b70a9" data-status="b653b1cf5d56ba95b6da248e123b70a9-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="b653b1cf5d56ba95b6da248e123b70a9-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddOpenApi</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Environment</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IsDevelopment</span><span style="color: hsl(var(--syntax-invalid-deprecated))">())</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapOpenApi</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapGet</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;/module-a&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Project</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ModuleA</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ResponseData</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Hello from Module A&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">100</span><span style="color: hsl(var(--syntax-invalid-deprecated))">));</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapGet</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;/module-b&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Project</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ModuleB</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ResponseData</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Hello from Module B&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Success&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">67</span><span style="color: hsl(var(--syntax-invalid-deprecated))">));</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>The above endpoints generates an OpenAPI document similar to the following one.
Within the <code>components/schemas</code> section, you&#39;ll notice that only the last occurence of the <code>ResponseData</code> model is documented, and both endpoints reference the same schema:</p><pre id="4193d470b827c013aa53ea1b9d4c298b"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.77778 6.66667C4.77778 5.19391 5.97169 4 7.44444 4C7.93536 4 8.33333 4.39797 8.33333 4.88889C8.33333 5.3798 7.93536 5.77778 7.44444 5.77778C6.95353 5.77778 6.55556 6.17575 6.55556 6.66667V10.1675C6.55556 10.8682 6.28251 11.5173 5.82622 12C6.28251 12.4827 6.55556 13.1318 6.55556 13.8325V17.3333C6.55556 17.8243 6.95353 18.2222 7.44444 18.2222C7.93536 18.2222 8.33333 18.6202 8.33333 19.1111C8.33333 19.602 7.93536 20 7.44444 20C5.97169 20 4.77778 18.8061 4.77778 17.3333V13.8325C4.77778 13.4246 4.50018 13.0691 4.10448 12.9701L3.6733 12.8623C3.2776 12.7635 3 12.4079 3 12C3 11.5921 3.2776 11.2365 3.6733 11.1377L4.10448 11.0299C4.50018 10.9309 4.77778 10.5754 4.77778 10.1675V6.66667ZM19 6.66667C19 5.19391 17.8061 4 16.3333 4C15.8424 4 15.4444 4.39797 15.4444 4.88889C15.4444 5.3798 15.8424 5.77778 16.3333 5.77778C16.8243 5.77778 17.2222 6.17575 17.2222 6.66667V10.1675C17.2222 10.8682 17.4953 11.5173 17.9516 12C17.4953 12.4827 17.2222 13.1318 17.2222 13.8325V17.3333C17.2222 17.8243 16.8243 18.2222 16.3333 18.2222C15.8424 18.2222 15.4444 18.6202 15.4444 19.1111C15.4444 19.602 15.8424 20 16.3333 20C17.8061 20 19 18.8061 19 17.3333V13.8325C19 13.4246 19.2776 13.0691 19.6733 12.9701L20.1044 12.8623C20.5002 12.7635 20.7778 12.4079 20.7778 12C20.7778 11.5921 20.5002 11.2365 20.1044 11.1377L19.6733 11.0299C19.2776 10.9309 19 10.5754 19 10.1675V6.66667Z" fill="#8B5CF6"/>
</svg>
 <span class="file-name">before.json</span> <button type="button" class="copy-code icon align-text-top" data-ref="4193d470b827c013aa53ea1b9d4c298b" data-status="4193d470b827c013aa53ea1b9d4c298b-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="4193d470b827c013aa53ea1b9d4c298b-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="json dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">openapi</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;3.1.1&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">info</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">title</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Project | v1&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">version</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;1.0.0&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">servers</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">url</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;http://localhost:5294/&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">paths</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">/module-a</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">get</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">tags</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Project&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">responses</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">200</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">description</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OK&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">content</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">application/json</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">schema</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">                  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">$ref</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;#/components/schemas/ResponseData&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">/module-b</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">get</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">tags</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Project&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">responses</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">200</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">description</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OK&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">content</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">application/json</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">schema</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">                  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">$ref</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;#/components/schemas/ResponseData&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">components</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">schemas</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">ResponseData</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">required</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;message&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;code&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;object&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">message</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;string&quot;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">code</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">pattern</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;^-?(?:0|[1-9]</span><span style="color: hsl(var(--syntax-constant))">\\</span><span style="color: hsl(var(--syntax-meta-property-value-css))">d*)$&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;integer&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;string&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">format</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;int32&quot;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">tags</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Project&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">]</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre>
		<h2 id="resolving-naming-conflicts-using-createschemareferenceid">
		  <a href="#resolving-naming-conflicts-using-createschemareferenceid" class="anchor mark-hover" tabindex="-1">Resolving Naming Conflicts using CreateSchemaReferenceId</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>This is not what we want. To resolve this, we can customize the schema naming strategy using the <code>CreateSchemaReferenceId</code> option when configuring OpenAPI in ASP.NET.</p><pre id="b5abd4713013c6fbcb5aac61889f389c"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="b5abd4713013c6fbcb5aac61889f389c" data-status="b5abd4713013c6fbcb5aac61889f389c-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="b5abd4713013c6fbcb5aac61889f389c-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddOpenApi</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">CreateSchemaReferenceId</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">schemaRefId</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">OpenApiOptions</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateDefaultSchemaReferenceId</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">        //</span><span style="color: hsl(var(--syntax-comment))"> Ignore primitive types</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">schemaRefId</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">is</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">null</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">null</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">        //</span><span style="color: hsl(var(--syntax-comment))"> Replace '+' with '.' to handle nested types</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">type</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Type</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">FullName</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Replace</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;+&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;.&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">StringComparison</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Ordinal</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div></code></pre><p>With this configuration, the <code>CreateSchemaReferenceId</code> method generates schema names that include the full namespace of the model, effectively avoiding naming conflicts. Once you apply this change, the generated OpenAPI document will look like this:</p><pre id="e9a0ba9e23687a2a01c4793c76ee18c8"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.77778 6.66667C4.77778 5.19391 5.97169 4 7.44444 4C7.93536 4 8.33333 4.39797 8.33333 4.88889C8.33333 5.3798 7.93536 5.77778 7.44444 5.77778C6.95353 5.77778 6.55556 6.17575 6.55556 6.66667V10.1675C6.55556 10.8682 6.28251 11.5173 5.82622 12C6.28251 12.4827 6.55556 13.1318 6.55556 13.8325V17.3333C6.55556 17.8243 6.95353 18.2222 7.44444 18.2222C7.93536 18.2222 8.33333 18.6202 8.33333 19.1111C8.33333 19.602 7.93536 20 7.44444 20C5.97169 20 4.77778 18.8061 4.77778 17.3333V13.8325C4.77778 13.4246 4.50018 13.0691 4.10448 12.9701L3.6733 12.8623C3.2776 12.7635 3 12.4079 3 12C3 11.5921 3.2776 11.2365 3.6733 11.1377L4.10448 11.0299C4.50018 10.9309 4.77778 10.5754 4.77778 10.1675V6.66667ZM19 6.66667C19 5.19391 17.8061 4 16.3333 4C15.8424 4 15.4444 4.39797 15.4444 4.88889C15.4444 5.3798 15.8424 5.77778 16.3333 5.77778C16.8243 5.77778 17.2222 6.17575 17.2222 6.66667V10.1675C17.2222 10.8682 17.4953 11.5173 17.9516 12C17.4953 12.4827 17.2222 13.1318 17.2222 13.8325V17.3333C17.2222 17.8243 16.8243 18.2222 16.3333 18.2222C15.8424 18.2222 15.4444 18.6202 15.4444 19.1111C15.4444 19.602 15.8424 20 16.3333 20C17.8061 20 19 18.8061 19 17.3333V13.8325C19 13.4246 19.2776 13.0691 19.6733 12.9701L20.1044 12.8623C20.5002 12.7635 20.7778 12.4079 20.7778 12C20.7778 11.5921 20.5002 11.2365 20.1044 11.1377L19.6733 11.0299C19.2776 10.9309 19 10.5754 19 10.1675V6.66667Z" fill="#8B5CF6"/>
</svg>
 <span class="file-name">after.json</span> <button type="button" class="copy-code icon align-text-top" data-ref="e9a0ba9e23687a2a01c4793c76ee18c8" data-status="e9a0ba9e23687a2a01c4793c76ee18c8-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="e9a0ba9e23687a2a01c4793c76ee18c8-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="json dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">openapi</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;3.1.1&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">info</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">title</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Project | v1&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">version</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;1.0.0&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">servers</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">url</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;http://localhost:5294/&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">paths</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">/module-a</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">get</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">tags</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Project&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">responses</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">200</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">description</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OK&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">content</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">application/json</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">schema</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">$ref</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;#/components/schemas/Project.ModuleA.ResponseData&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">/module-b</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">get</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">tags</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Project&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">responses</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">200</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">description</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OK&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">content</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">application/json</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">schema</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">$ref</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;#/components/schemas/Project.ModuleB.ResponseData&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">components</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">schemas</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">Project.ModuleA.ResponseData</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">required</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;message&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;code&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;object&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">message</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;string&quot;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">code</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">pattern</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;^-?(?:0|[1-9]</span><span style="color: hsl(var(--syntax-constant))">\\</span><span style="color: hsl(var(--syntax-meta-property-value-css))">d*)$&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;integer&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;string&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">format</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;int32&quot;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">Project.ModuleB.ResponseData</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">required</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;description&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;status&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;value&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;object&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">description</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;string&quot;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">status</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;string&quot;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">pattern</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;^-?(?:0|[1-9]</span><span style="color: hsl(var(--syntax-constant))">\\</span><span style="color: hsl(var(--syntax-meta-property-value-css))">d*)$&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;integer&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;string&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">],</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">format</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;int32&quot;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">tags</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-entity-name-section))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Project&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">]</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Using FusionCache's Backplane to synchronize HybridCache instances across multiple instances]]></title>
					<description><![CDATA[HybridCache is a great caching solution but it doesn't sync in-memory caches across instances. Discover how FusionCache can help you achieve this (and much more) with its Backplane feature.]]></description>
					<link>https://timdeschryver.dev/blog/hybridcache-sync-with-fusioncache-backplane</link>
					<guid>https://timdeschryver.dev/blog/hybridcache-sync-with-fusioncache-backplane</guid>
					<pubDate>Fri, 16 Jan 2026 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/hybridcache-sync-with-fusioncache-backplane/images/banner.webp" alt="Using FusionCache's Backplane to synchronize HybridCache instances across multiple instances"/><p>Read <strong>Using FusionCache's Backplane to synchronize HybridCache instances across multiple instances</strong> on <a href="https://timdeschryver.dev/blog/hybridcache-sync-with-fusioncache-backplane">timdeschryver.dev</a></p><p>The decision of whether to use a cache or not depends entirely on your application&#39;s needs, but if you do require caching, then .NET provides several options to choose from.</p>
		<h2 id="caching-in-aspnet">
		  <a href="#caching-in-aspnet" class="anchor mark-hover" tabindex="-1">Caching in ASP.NET</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Previously, you could choose between using an in-memory cache (<a class="mark mark-hover" href="https://learn.microsoft.com/en-us/aspnet/core/performance/caching/memory?WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'><code>IMemoryCache</code></a>) or a distributed cache (<a class="mark mark-hover" href="https://learn.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-10.0&WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'><code>IDistributedCache</code></a>) based on your requirements. The recent addition of the <a class="mark mark-hover" href="https://learn.microsoft.com/en-us/aspnet/core/performance/caching/hybrid?WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'><code>HybridCache</code></a> - in .NET 9 - combines the best of both worlds, and provides a simple and consistent API for developers.</p><p>The <code>HybridCache</code> works by first checking the in-memory cache (L1) for the requested item. If it&#39;s not found there, it then checks the distributed cache (L2). If the item is found in the distributed cache, it is then added to the in-memory cache for faster access when it&#39;s read another time. This approach ensures that frequently accessed data is quickly available while still maintaining consistency across different instances of your application.</p><p>Next to the speed improvements and the DX, the <code>HybridCache</code> also has the following features:</p><ul>
<li>Cache-stampede protection: Prevents multiple requests from overwhelming the cache when an item expires.</li>
<li>Cache invalidation: use tags to invalidate related cache entries across both caches.</li>
<li>Configurable: define where you want to cache (L1 and/or L2) your data and how it should be serialized.</li>
</ul>

		<h2 id="using-hybridcache">
		  <a href="#using-hybridcache" class="anchor mark-hover" tabindex="-1">Using HybridCache</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2>
		<h3 id="installation-and-setup">
		  <a href="#installation-and-setup" class="anchor mark-hover" tabindex="-1">Installation and setup</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>To start using the <code>HybridCache</code>, you need to install the <a class="mark mark-hover" href="https://www.nuget.org/packages/Microsoft.Extensions.Caching.Hybrid" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.nuget.org)'>Microsoft.Extensions.Caching.Hybrid package</a>.</p><pre id="e0750cd017e6ebbf04241422a67cf11e"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="e0750cd017e6ebbf04241422a67cf11e" data-status="e0750cd017e6ebbf04241422a67cf11e-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="e0750cd017e6ebbf04241422a67cf11e-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">Microsoft.Extensions.Caching.Hybrid</span></div></code></pre><p>Next, register the <code>HybridCache</code> using the <code>AddHybridCache()</code> method in your the <code>Program.cs</code> file:</p><pre id="90806eb84ff9dab136a297b8f75f0e9e"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="90806eb84ff9dab136a297b8f75f0e9e" data-status="90806eb84ff9dab136a297b8f75f0e9e-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="90806eb84ff9dab136a297b8f75f0e9e-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="csharp dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddHybridCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>The above registers the <code>HybridCache</code> as an in-memory cache.
When a distributed cache implementation (like Redis or SQL Server) is in the dependency injection container, it will automatically be used as the distributed cache.
For example, to use Redis as the distributed cache, you can register it like this:</p><pre id="799daecc393ddd10c8894ed457fcec1c"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="799daecc393ddd10c8894ed457fcec1c" data-status="799daecc393ddd10c8894ed457fcec1c-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="799daecc393ddd10c8894ed457fcec1c-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="csharp dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddStackExchangeRedisCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Configuration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;localhost:6379&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddHybridCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p><code>AddHybridCache()</code> can also take a callback action to customize the behavior of the cache by providing <code>HybridCacheOptions</code>. For example, to disable the local cache and set a custom expiration time for cache entries, you can do the following.</p><pre id="ef5b8e8971dc894b145842d5f44a5ebd"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="ef5b8e8971dc894b145842d5f44a5ebd" data-status="ef5b8e8971dc894b145842d5f44a5ebd-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="ef5b8e8971dc894b145842d5f44a5ebd-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="csharp dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddHybridCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">DefaultEntryOptions</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">HybridCacheEntryOptions</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">Flags</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">HybridCacheEntryFlags</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">DisableLocalCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">Expiration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">TimeSpan</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">FromMinutes</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">5</span><span style="color: hsl(var(--syntax-invalid-deprecated))">),</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>Instead of configuring the expiration time for each cache entry individually, it&#39;s useful to set default options for your distributed cache entries using these options.</p>
		<h3 id="retrieving-and-writing-from-to-the-hybridcache">
		  <a href="#retrieving-and-writing-from-to-the-hybridcache" class="anchor mark-hover" tabindex="-1">Retrieving and writing from/to the HybridCache</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>To consume the <code>HybridCache</code>, inject a <code>HybridCache</code> instance and use the <code>GetOrCreateAsync</code> method to retrieve or create cache entries.
This methods requires a cache key and a factory function to create the item if it doesn&#39;t exist in the cache.</p><pre id="f5bb23a0c5b22895802d4f7d53ebf205"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">DataEndpoint.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="f5bb23a0c5b22895802d4f7d53ebf205" data-status="f5bb23a0c5b22895802d4f7d53ebf205-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="f5bb23a0c5b22895802d4f7d53ebf205-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="csharp " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapGet</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;/data&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-name-section))">HybridCache</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">cache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">data</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">cache</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetOrCreateAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;data&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">cancellationToken</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">result</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">/*</span><span style="color: hsl(var(--syntax-comment))"> Do some expensive operation to fetch data </span><span style="color: hsl(var(--syntax-invalid-deprecated))">*/</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">result</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">data</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div></code></pre><p><code>GetOrCreateAsync</code> also accepts optional <code>options</code> argument (<a class="mark mark-hover" href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.hybrid.hybridcacheentryoptions?WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'><code>HybridCacheEntryOptions</code></a>) to customize the caching behavior for that specific entry, such as setting expiration times, and an optional <code>tags</code> argument to associate tags with the cache entry.</p><p>Besides the <code>GetOrCreateAsync</code> method, the <code>HybridCache</code> also provides methods to remove cache entries (<code>RemoveAsync</code> and <code>RemoveByTagAsync</code>), as well as a method to set cache entries directly (<code>SetAsync</code>).</p><p>For those looking to migrate, it should be relatively straightforward to migrate to the <code>HybridCache</code> as it is a drop-in replacement for both <code>IMemoryCache</code> and <code>IDistributedCache</code>, especially if you are already using the memory cache because it has a similar API.</p>
		<h2 id="problem-keeping-multiple-in-memory-caches-in-sync">
		  <a href="#problem-keeping-multiple-in-memory-caches-in-sync" class="anchor mark-hover" tabindex="-1">Problem: Keeping multiple in-memory caches in sync</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>But this also raises the question, how do you keep the cache in sync for different in-memory instances, for example when you have multiple instances of the same application running?</p><p>Sadly, the <code>HybridCache</code> does not provide a built-in solution for this scenario yet. There&#39;s an <a class="mark mark-hover" href="https://github.com/dotnet/extensions/issues/5517" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>open issue</a> on GitHub discussing this exact problem.</p><p>One possible approach to tackle this problem is to use the Redis Pub/Sub messaging system to handle cache invalidation across different instances.</p>
		<h2 id="fusioncache">
		  <a href="#fusioncache" class="anchor mark-hover" tabindex="-1">FusionCache</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Another approach is to introduce the <a class="mark mark-hover" href="https://github.com/ZiggyCreatures/FusionCache" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>FusionCache</a> package, which is a third-party caching library that provides advanced features to have a resilient and flexible caching solution. It supports more advanced features compared to <code>HybridCache</code>, such as <a class="mark mark-hover" href="https://github.com/ZiggyCreatures/FusionCache/blob/main/docs/Backplane.md" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'><code>Backplane</code></a>, which can be used to synchronize cache entries across multiple instances using a distributed messaging system. For a full comparison between different caching libraries, check out <a class="mark mark-hover" href="https://github.com/ZiggyCreatures/FusionCache/blob/main/docs/Comparison.md" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>the comparison table</a>.</p><div class="custom-block info">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z"/><path d="M15 3v4a2 2 0 0 0 2 2h4"/></svg></span>Note</div>
			<p>We&#39;re not going into all of FusionCache&#39;s features in this post, as that could be a blog post on its own. If you want to know more about it, check out the <a class="mark mark-hover" href="https://github.com/ZiggyCreatures/FusionCache/blob/main/docs/README.md" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>documentation</a>, which is very well written and provides many examples.</p>
		</div><p>Luckily, FusionCache also provides a <code>HybridCache</code> wrapper so you don&#39;t have to change your existing codebase if there&#39;s already a <code>HybridCache</code> being used.</p>
		<h3 id="setup">
		  <a href="#setup" class="anchor mark-hover" tabindex="-1">Setup</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>To install the core library of FusionCache as a distributed cache install the following packages:</p><pre id="1756105df41847eacd77c1dd09b431a2"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="1756105df41847eacd77c1dd09b431a2" data-status="1756105df41847eacd77c1dd09b431a2-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="1756105df41847eacd77c1dd09b431a2-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">ZiggyCreatures.FusionCache</span></div><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">ZiggyCreatures.FusionCache.Serialization.SystemTextJson</span></div></code></pre><p>To register FusionCache in your application, use the <code>AddFusionCache()</code> method, which sets up FusionCache as an in-memory cache.
To configure FusionCache to use a distributed cache (like Redis), you can use the <code>WithDistributedCache()</code> method, which requires you to also configure the serializer using the <code>WithSerializer()</code> method.
Because we want it to act as a <code>HybridCache</code> wrapper, use the <code>AsHybridCache()</code> method.</p><pre id="663855c040186c7231b7da2c3b0171cc"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="663855c040186c7231b7da2c3b0171cc" data-status="663855c040186c7231b7da2c3b0171cc-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="663855c040186c7231b7da2c3b0171cc-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="csharp dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddFusionCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithOptions</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">        //</span><span style="color: hsl(var(--syntax-comment))"> Configure FusionCache options here, enable/disable features as needed</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithSerializer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">FusionCacheSystemTextJsonSerializer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithDistributedCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">RedisCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">RedisCacheOptions</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Configuration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;localhost:6379&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AsHybridCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>The rest of the code remains the same, as you can keep using the <code>HybridCache</code> API.</p>
		<h3 id="backplane-support">
		  <a href="#backplane-support" class="anchor mark-hover" tabindex="-1">Backplane support</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Backplane support can simply be activated using the <code>WithBackplane()</code> method while configuring FusionCache.</p><p>Before using the backplane feature, you need to install the appropriate backplane package.
In this case, we use the Redis backplane implementation, so install the following package:</p><pre id="0e479485c8b8a514d55359755fe7fbc7"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="0e479485c8b8a514d55359755fe7fbc7" data-status="0e479485c8b8a514d55359755fe7fbc7-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="0e479485c8b8a514d55359755fe7fbc7-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis</span></div></code></pre><pre id="f6ceaddc17ff20f260f7afeb9c946205"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">Program.cs</span> <button type="button" class="copy-code icon align-text-top" data-ref="f6ceaddc17ff20f260f7afeb9c946205" data-status="f6ceaddc17ff20f260f7afeb9c946205-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="f6ceaddc17ff20f260f7afeb9c946205-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="csharp dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddFusionCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithOptions</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">        //</span><span style="color: hsl(var(--syntax-comment))"> Configure FusionCache options here, enable/disable features as needed</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithSerializer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">FusionCacheSystemTextJsonSerializer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithDistributedCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">RedisCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">RedisCacheOptions</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Configuration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;localhost:6379&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">WithBackplane</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">RedisBackplane</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">RedisBackplaneOptions</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Configuration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;localhost:6379&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AsHybridCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>With this setup, whenever a cache entry is removed or updated in one instance, a message is published, which notifies all other instances to invalidate their local caches accordingly. This ensures that all instances have consistent cache data.</p>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>If you&#39;re considering using caching in your .NET applications, the <code>HybridCache</code> is a great choice due to its simplicity and built-in features. You can get started quickly with minimal setup and enjoy the benefits of in-memory caching, while still having a distributed cache as an option for larger-scale deployments. Changing the caching mechanism can be done in a central place, without affecting the rest of your application code.</p><p>The two-tier (in-memory and distributed) caching approach of <code>HybridCache</code> provides a good balance between performance and scalability.</p><p>However, it might not offer all the features that you need. This is where FusionCache comes in.
Because FusionCache provides a wrapper for <code>HybridCache</code>, it should be easy to integrate it into your existing applications and migrate to it.</p><p>In this post we&#39;ve seen how to use FusionCache&#39;s backplane feature to keep multiple in-memory caches synchronized across different instances of your application. The best part is that this requires minimal code changes—just a few extra configuration lines during setup.</p><p>If you&#39;re running a distributed application with multiple instances and need consistent cache data across all of them, combining the <code>HybridCache</code> API with FusionCache is a practical approach that doesn&#39;t require a complete rewrite of your caching logic. As an added bonus, you&#39;ll also gain access to FusionCache&#39;s other resilience features should you need them in the future.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Named global query filters in Entity Framework Core 10]]></title>
					<description><![CDATA[Entity Framework Core 10 enhances the global query filters feature, allowing you to define multiple filters per entity and selectively disable them by name. This powerful enhancement gives you fine-grained control over query filtering, perfect for implementing soft deletes.]]></description>
					<link>https://timdeschryver.dev/blog/named-global-query-filters-in-entity-framework-core-10</link>
					<guid>https://timdeschryver.dev/blog/named-global-query-filters-in-entity-framework-core-10</guid>
					<pubDate>Thu, 04 Dec 2025 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/named-global-query-filters-in-entity-framework-core-10/images/banner.webp" alt="Named global query filters in Entity Framework Core 10"/><p>Read <strong>Named global query filters in Entity Framework Core 10</strong> on <a href="https://timdeschryver.dev/blog/named-global-query-filters-in-entity-framework-core-10">timdeschryver.dev</a></p><p>Previous version of Entity Framework included a way to define global query filters while configuring the model. These filters were applied to all queries for a given entity type, allowing developers to implement features like soft deletes easily. However, this feature was limited in flexibility, as it did not allow multiple filters.</p><p>With Entity Framework 10, a new feature has been introduced that allows developers to define named global query filters. This enhancement provides greater flexibility by enabling multiple filters to be defined, which can be selectively disabled based on their names.</p><div class="custom-block info">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z"/><path d="M15 3v4a2 2 0 0 0 2 2h4"/></svg></span>Note</div>
			<p>For the entire change list of new features in Entity Framework 10, refer to the <a class="mark mark-hover" href="https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/whatsnew?WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'>official documentation</a>.</p>
		</div>
		<h2 id="before-single-global-query-filter">
		  <a href="#before-single-global-query-filter" class="anchor mark-hover" tabindex="-1">Before: Single Global Query Filter</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>In previous versions, you could define a single global query filter using the <code>HasQueryFilter</code> method. For example, to implement a soft delete feature, you might have done something like this:</p><pre id="becb89e436cf751d0612653ef6ea7a7d"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="becb89e436cf751d0612653ef6ea7a7d" data-status="becb89e436cf751d0612653ef6ea7a7d-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="becb89e436cf751d0612653ef6ea7a7d-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="csharp " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerContext</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">DbContext</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">protected</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">override</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">void</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">OnModelCreating</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-name-section))">ModelBuilder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">modelBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">modelBuilder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Entity</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Customer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">HasQueryFilter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">p</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">p</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IsDeleted</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Or as a IEntityTypeConfiguration&lt;T&gt; implementation</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerConfiguration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IEntityTypeConfiguration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Customer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">void</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">Configure</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-name-section))">EntityTypeBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Customer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">HasQueryFilter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">p</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">p</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IsDeleted</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>The filter automatically excludes any <code>Customer</code> entities marked as deleted from all queries. If you wanted to include an additional filter, you would have to combine it with the existing one. For example, let&#39;s say you wanted to filter customers based on their status as well:</p><pre id="bca8633ee836f4c7e7b4b7ed38fd6e5c"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="bca8633ee836f4c7e7b4b7ed38fd6e5c" data-status="bca8633ee836f4c7e7b4b7ed38fd6e5c-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="bca8633ee836f4c7e7b4b7ed38fd6e5c-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerConfiguration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IEntityTypeConfiguration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Customer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">void</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">Configure</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-name-section))">EntityTypeBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Customer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">        //</span><span style="color: hsl(var(--syntax-comment))"> This does not work as expected</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">        //</span><span style="color: hsl(var(--syntax-comment))"> The last filter defined overrides the previous one</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">HasQueryFilter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">p</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">p</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IsDeleted</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">HasQueryFilter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">p</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">p</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Status</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">CustomerStatus</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Archived</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">        //</span><span style="color: hsl(var(--syntax-comment))"> Instead, combine both conditions into a single filter</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">HasQueryFilter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">p</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">p</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IsDeleted</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">&amp;&amp;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">p</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Status</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">CustomerStatus</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Archived</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>For scenarios where you needed to disable the global filter in specific queries, you had to use the <code>IgnoreQueryFilters</code> method, which disabled all global filters for that query:</p><pre id="08598e04ac1fcab5b986bbc793496313"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="08598e04ac1fcab5b986bbc793496313" data-status="08598e04ac1fcab5b986bbc793496313-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="08598e04ac1fcab5b986bbc793496313-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Filtered query, customers that are soft-deleted or archived are excluded</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">activeCustomers</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Customers</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">ToListAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">cancellationToken</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Unfiltered query, all customers are included</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">customers</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Customers</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IgnoreQueryFilters</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">ToListAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">cancellationToken</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div></code></pre>
		<h2 id="now-named-global-query-filters">
		  <a href="#now-named-global-query-filters" class="anchor mark-hover" tabindex="-1">Now: Named Global Query Filters</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>With Entity Framework Core 10, you can now define named global query filters. This allows you to create multiple filters for the same entity type and selectively disable them by name (one or more).</p><p>The refactored code for the previous example would look like this.
Instead of combining multiple conditions into a single filter, invoke <code>HasQueryFilter</code> multiple times and provide a unique name for each filter.</p><pre id="619c52c24acda39728a2f39629787c07"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="619c52c24acda39728a2f39629787c07" data-status="619c52c24acda39728a2f39629787c07-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="619c52c24acda39728a2f39629787c07-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="csharp " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerConfiguration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IEntityTypeConfiguration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Customer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">void</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">Configure</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-name-section))">EntityTypeBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Customer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">HasQueryFilter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;SoftDeleteFilter&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">p</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">p</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IsDeleted</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">HasQueryFilter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;StatusFilter&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">p</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">p</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Status</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">CustomerStatus</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Archived</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>It&#39;s still possible to create a global filter without a name, which behaves as before.
But, you can&#39;t use both named and unnamed filters for the same entity type.</p><p>Now to disable specific filters in a query, you can use the <code>IgnoreQueryFilters</code> method and provide the name(s) of the filters you want to disable.</p><pre id="abeea3b7169ec0969844b87f86a1efc6"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="abeea3b7169ec0969844b87f86a1efc6" data-status="abeea3b7169ec0969844b87f86a1efc6-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="abeea3b7169ec0969844b87f86a1efc6-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="csharp " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Filtered query, customers that are soft-deleted or archived are excluded (same as before)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">activeCustomers</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Customers</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">ToListAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">cancellationToken</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Unfiltered query, all customers are included (same as before)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">customers</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Customers</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IgnoreQueryFilters</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">ToListAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">cancellationToken</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Filtered query, customers that are soft-deleted are excluded, but archived customers are included</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">customersWithArchived</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Customers</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IgnoreQueryFilters</span><span style="color: hsl(var(--syntax-invalid-deprecated))">([</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;StatusFilter&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">])</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">ToListAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">cancellationToken</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Unfiltered query, both soft-deleted and archived customers are included because both filters are ignored</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">customersAll</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Customers</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IgnoreQueryFilters</span><span style="color: hsl(var(--syntax-invalid-deprecated))">([</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;StatusFilter&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;SoftDeleteFilter&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">])</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">ToListAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">cancellationToken</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div></code></pre>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Using cookies for authentication in an Angular application]]></title>
					<description><![CDATA[Learn how to simplify authentication in Angular applications by leveraging secure, cookie-based authentication through a Backend for Frontend (BFF) pattern. This approach eliminates the complexity of client-side token management while improving the security of your client-side code.]]></description>
					<link>https://timdeschryver.dev/blog/using-cookies-for-authentication-in-an-angular-application</link>
					<guid>https://timdeschryver.dev/blog/using-cookies-for-authentication-in-an-angular-application</guid>
					<pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/using-cookies-for-authentication-in-an-angular-application/images/banner.webp" alt="Using cookies for authentication in an Angular application"/><p>Read <strong>Using cookies for authentication in an Angular application</strong> on <a href="https://timdeschryver.dev/blog/using-cookies-for-authentication-in-an-angular-application">timdeschryver.dev</a></p><p>In the prevous posts of this series, we&#39;ve already seen how to set up a Backend for Frontend (BFF) using Aspire and how to implement a secure authentication flow for the .NET backend of the application. In this post, we will focus on the Angular frontend, and how to properly handle authentication.</p>
		<h2 id="code-flow-with-pkce">
		  <a href="#code-flow-with-pkce" class="anchor mark-hover" tabindex="-1">Code flow with PKCE</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>When building a secure Angular, or any SPA , application, there are two main approaches to handle authentication: using the Authorization Code flow with PKCE or using cookies.
With the Authorization Code flow with PKCE, the frontend application is responsible for the entire authentication process. The process starts to redirect the user to the identity provider, handles the authorization code exchange, and manages tokens (access and refresh tokens) on the client side.</p><p>Because this is very specific (complex) code, but yet very important to get right, many choose to use libraries that abstract away the complexity.
In Angular, we can use libraries like <a class="mark mark-hover" href="https://angular-auth-oidc-client.com/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular-auth-oidc-client.com)'>angular-auth-oidc-client</a> which simplifies the implementation of the Authorization Code flow with PKCE.</p><p>However, this approach has some downsides:</p><ul>
<li><strong>Security risks</strong>: Storing tokens in the browser (e.g., in local storage or session storage) can expose them to XSS attacks.</li>
<li><strong>Token management</strong>: Handling token refresh and expiration can be challenging on the client side.</li>
<li><strong>CORS issues</strong>: Making API calls to a backend from a different origin can lead to CORS issues that need to be managed.</li>
<li><strong>Bigger bundle size</strong>: Including authentication libraries can increase the size of the frontend application.</li>
</ul>

		<h2 id="using-cookies-for-authentication">
		  <a href="#using-cookies-for-authentication" class="anchor mark-hover" tabindex="-1">Using cookies for authentication</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>An alternative approach is to use cookies for authentication. In this approach, the backend (BFF) handles the authentication process and manages the authentication state.</p><p>While it&#39;s possible to read the cookies&#39; content using JavaScript, it&#39;s generally not recommended to have the frontend application directly access (authentication) cookies. That&#39;s why I mentioned in the previous post to set the cookies as <a class="mark mark-hover" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fdeveloper.mozilla.org)'><code>HttpOnly</code></a> and <a class="mark mark-hover" href="https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Cookies" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fdeveloper.mozilla.org)'><code>Secure</code></a>, which prevents JavaScript from accessing them, enhancing the security.</p><p>Instead, the cookie should not be directly consumed by the frontend application. The backend is responsible for setting and reading the authentication cookies. The frontend application simply makes requests to the backend to access the authenticated state, just as it would with any other API resource.</p><p>While doing so, we avoid the downsides of the Authorization Code flow with PKCE. The only prerequisite is that the frontend and backend are served from the same domain, or that the cookie is configured to be sent across domains (using the <a class="mark mark-hover" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#samesitesamesite-value" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fdeveloper.mozilla.org)'><code>SameSite=None; Secure</code> attribute</a>).</p>
		<h2 id="implementing-authentication-in-angular">
		  <a href="#implementing-authentication-in-angular" class="anchor mark-hover" tabindex="-1">Implementing authentication in Angular</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The good news is that implementing cookie-based authentication in an Angular application is straightforward. The main idea is to create an authentication service that communicates with the backend to determine the user&#39;s authentication status.
Let&#39;s take a look at how we can implement this in an Angular application.</p><p>The main concerns of the authentication implementation are the login and logout methods, and a way to fetch the authenticated user&#39;s information.
From a technical side, these can all be implemented within a single service that communicates with the backend.
In the example below, this service communicates with the <code>/bff</code> endpoints to perform these actions.
The <code>login</code> and <code>logout</code> methods simply redirect the user to the corresponding backend endpoints, while the <code>user</code> property fetches the authenticated user&#39;s information.</p>
        <div class="code-group">
			<div class="code-group-tabs" role="tablist" aria-label="Code examples"> <button type="button" id="code-tab-189" data-id="189" class="code-group-tab active" role="tab" aria-selected="true" aria-controls="code-panel-189" tabindex="0">Authentication Service</button><button type="button" id="code-tab-190" data-id="190" class="code-group-tab " role="tab" aria-selected="false" aria-controls="code-panel-190" tabindex="-1">User Model</button></div>
			<div id="code-panel-189" data-id="189" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-189" ><pre id="6cba1d2bb196ccc6d9131363cf54e1e2"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">authentication.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/authentication/authentication.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="6cba1d2bb196ccc6d9131363cf54e1e2" data-status="6cba1d2bb196ccc6d9131363cf54e1e2-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="6cba1d2bb196ccc6d9131363cf54e1e2-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">@</span><span style="color: hsl(var(--syntax-constant-numeric))">Injectable</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  providedIn</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'root'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Authentication</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">private</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">document</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">inject</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">DOCUMENT</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">private</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">_user</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">httpResource</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'/bff/user'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    parse</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">parse</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">User</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">asReadonly</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">readonly</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">user</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">computed</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">_user</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">value</span><span style="color: hsl(var(--syntax-meta-tag))">())</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">login</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">redirectUrl</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">void</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">document</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">location</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">href</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">`/bff/login?returnUrl=</span><span style="color: hsl(var(--syntax-invalid-deprecated))">${</span><span style="color: hsl(var(--syntax-meta-tag))">redirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">`</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">logout</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">redirectUrl</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">void</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">document</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">location</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">href</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">`/bff/logout?returnUrl=</span><span style="color: hsl(var(--syntax-invalid-deprecated))">${</span><span style="color: hsl(var(--syntax-meta-tag))">redirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">`</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre></div><div id="code-panel-190" data-id="190" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-190" hidden><pre id="3ecd1c5252cee81b17a5d53965307e1b"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">user.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/authentication/user.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="3ecd1c5252cee81b17a5d53965307e1b" data-status="3ecd1c5252cee81b17a5d53965307e1b-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="3ecd1c5252cee81b17a5d53965307e1b-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">User</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">z</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">strictObject</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  isAuthenticated</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">z</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">boolean</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">z</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">nullable</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">z</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">string</span><span style="color: hsl(var(--syntax-meta-tag))">())</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  claims</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">z</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">array</span><span style="color: hsl(var(--syntax-meta-tag))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">z</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">strictObject</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">z</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">string</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">z</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">string</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  )</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">type</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">User</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">z</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">infer</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">typeof</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">User</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;;</span></div></code></pre></div>
		</div><p>Another important part of the authentication is to protect routes that require an authenticated user. We can achieve this by creating a <a class="mark mark-hover" href="https://angular.dev/guide/routing/route-guards" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'>route guard</a> that checks the user&#39;s authentication status before allowing access to certain routes, this is also a feature that&#39;s commonly provided by authentication libraries. If the user is not authenticated, the following guard will redirect the user to the login page.</p>
        <div class="code-group">
			<div class="code-group-tabs" role="tablist" aria-label="Code examples"> <button type="button" id="code-tab-191" data-id="191" class="code-group-tab active" role="tab" aria-selected="true" aria-controls="code-panel-191" tabindex="0">Guard</button><button type="button" id="code-tab-192" data-id="192" class="code-group-tab " role="tab" aria-selected="false" aria-controls="code-panel-192" tabindex="-1">Routes</button></div>
			<div id="code-panel-191" data-id="191" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-191" ><pre id="c394ad58a8ac4e5ba6968666cf787547"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">authenticated-guard.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/authentication/authenticated-guard.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="c394ad58a8ac4e5ba6968666cf787547" data-status="c394ad58a8ac4e5ba6968666cf787547-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="c394ad58a8ac4e5ba6968666cf787547-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">authenticatedGuard</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CanActivateFn</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> (</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">_next</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ActivatedRouteSnapshot</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">state</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">RouterStateSnapshot</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">) </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">authenticationService</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">inject</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">Authentication</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">toObservable</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">authenticationService</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">user</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">pipe</span><span style="color: hsl(var(--syntax-meta-tag))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">filterNullish</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">map</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">user</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> (</span><span style="color: hsl(var(--syntax-meta-tag))">user</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">isAuthenticated</span><span style="color: hsl(var(--syntax-meta-tag))">) </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-meta-tag))">authenticationService</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">login</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">state</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">url</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  )</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div></code></pre></div><div id="code-panel-192" data-id="192" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-192" hidden><pre id="c47e4025add20895784ba75b68786fbb"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">app.routes.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/app.routes.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="c47e4025add20895784ba75b68786fbb" data-status="c47e4025add20895784ba75b68786fbb-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="c47e4025add20895784ba75b68786fbb-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">routes</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Routes</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    path</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    canActivateChild</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span><span style="color: hsl(var(--syntax-meta-tag))">authenticatedGuard</span><span style="color: hsl(var(--syntax-meta-tag))">]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    children</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        path</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'customers'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant-numeric))">loadChildren</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">import</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@sandbox-app/customer-management/customer-management.routes'</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        title</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Customers'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    ]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre></div>
		</div><p>For the completeness of the implementation, here are the three corresponding backend endpoints that the Angular application communicates with for authentication. Most of the heavy lifting is done by ASP.NET itself.</p><pre id="76a40b2753b7669a87b4cf7543d4db23"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">UserEndpoints.cs</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Gateway/UserModule/UserModule.cs" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="76a40b2753b7669a87b4cf7543d4db23" data-status="76a40b2753b7669a87b4cf7543d4db23-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="76a40b2753b7669a87b4cf7543d4db23-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapGet</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;user&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-name-section))">ClaimsPrincipal</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">principal</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">user</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">principal</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">switch</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Identity</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IsAuthenticated</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">User</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">IsAuthenticated</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">Name</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">principal</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">FindFirstValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;name&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">Claims</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">principal</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Claims</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Select</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">c</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">UserClaim</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Type</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">c</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Value</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">c</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Value</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">_</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">User</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">IsAuthenticated</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">Name</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">null</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">TypedResults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Ok</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">user</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapGet</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;login&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">returnUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">claimsChallenge</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">HttpContext</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">context</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">properties</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">AuthenticationProperties</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">RedirectUri</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">BuildRedirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">returnUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">claimsChallenge</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">null</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">jsonString</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">claimsChallenge</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Replace</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;</span><span style="color: hsl(var(--syntax-constant))">\\</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">StringComparison</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Ordinal</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Trim</span><span style="color: hsl(var(--syntax-invalid-deprecated))">([</span><span style="color: hsl(var(--syntax-invalid-deprecated))">'</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">]);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">properties</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Items</span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;claims&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">]</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">jsonString</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">TypedResults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Challenge</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapGet</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;logout&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">redirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">HttpContext</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">context</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">properties</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">AuthenticationProperties</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">RedirectUri</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">BuildRedirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">redirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">TypedResults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">SignOut</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-tag))">CookieAuthenticationDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">OpenIdConnectDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">]);</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div></code></pre>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Using cookies for authentication in an Angular application is a secure and straightforward approach. By delegating the authentication process to the backend, we can avoid many of the complexity and security risks associated with client-side token management.</p><p>In this post we&#39;ve seen the most basic implementation of an authentication service in Angular that communicates with the backend to manage the user&#39;s authentication state, along with route guards to protect authenticated routes. This approach can further be extended with additional features such as role-based access control, or custom directives to show UI elements based on the user&#39;s authentication status.</p><p>The code samples provided in this post can be found in my <a class="mark mark-hover" href="https://github.com/timdeschryver/Sandbox" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>Sandbox repository on GitHub</a>.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Write strongly typed Web API integration tests using Kiota and OpenAPI]]></title>
					<description><![CDATA[Recently, I discovered Kiota, a tool that generates API clients from OpenAPI specifications, and it has significantly simplified my testing process. In this blog post, I'll share how I use Kiota to create type-safe HTTP clients that are consumed within the integration tests for my ASP.NET Web API endpoints.]]></description>
					<link>https://timdeschryver.dev/blog/write-strongly-typed-web-api-integration-tests-using-kiota-and-openapi</link>
					<guid>https://timdeschryver.dev/blog/write-strongly-typed-web-api-integration-tests-using-kiota-and-openapi</guid>
					<pubDate>Thu, 30 Oct 2025 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/write-strongly-typed-web-api-integration-tests-using-kiota-and-openapi/images/banner.webp" alt="Write strongly typed Web API integration tests using Kiota and OpenAPI"/><p>Read <strong>Write strongly typed Web API integration tests using Kiota and OpenAPI</strong> on <a href="https://timdeschryver.dev/blog/write-strongly-typed-web-api-integration-tests-using-kiota-and-openapi">timdeschryver.dev</a></p><p>I like to write integration tests for my ASP.NET Web API endpoints to ensure that they behave as expected. For most of the APIs I work with, I believe <strong>these tests are crucial for maintaining code quality and reliability</strong>.</p>
		<h2 id="the-problem-with-typical-integration-tests">
		  <a href="#the-problem-with-typical-integration-tests" class="anchor mark-hover" tabindex="-1">The problem with typical integration tests</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>An integration test typically hosts the API in a test server and fires (one or more) HTTP requests against it, verifying the responses against expected results.
To invoke the API, you have to manually create an HTTP request, setting the correct route and body for each request.</p><p>This practice can be tedious. It&#39;s easy to accidentally make a typo in the route, post a wrong model, or simply forget to update the test when the API changes. This can lead to some frustration and wasted time to figure out why the test is failing.</p><p>It can also lead to a bad design. To make testing easier, many tests reuse the API models as the request and response bodies. While this is convenient, it can be the cause to make changes to the production code simply to accommodate the tests. An example of this is that it&#39;s a good practice to treat the incoming request model as an immutable object, of which its properties cannot be modified. However, this makes it difficult to create test requests, where you might want to have the possibility to modify the request model. The same applies when you&#39;re using tools to dynamically create the models, such as <a class="mark mark-hover" href="https://github.com/AutoFixture/AutoFixture" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>AutoFixture</a>, which requires the models to have public setters. On the other hand, we also don&#39;t want to manually create separate request and response models just for testing purposes, as this leads to code duplication and maintenance overhead.</p>
		<h3 id="testcase-without-client-generation">
		  <a href="#testcase-without-client-generation" class="anchor mark-hover" tabindex="-1">Testcase without client generation</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Before diving into the solution, let&#39;s first look at a typical integration test without using a generated client.</p><p>The test below creates a new customer by sending an HTTP POST request, in the test:</p><ul>
<li>A customer is created using the constructor of the <code>CreateCustomer.Command</code> model, this model exists in the API layer</li>
<li>An HTTP POST request is sent to the <code>/customers</code> endpoint with the customer data in the request body, the route is hardcoded as a string</li>
</ul>
<pre id="75fff10f2af04f2582ce6fc2a918fd88"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="75fff10f2af04f2582ce6fc2a918fd88" data-status="75fff10f2af04f2582ce6fc2a918fd88-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="75fff10f2af04f2582ce6fc2a918fd88-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-entity-name-section))">Test</span><span style="color: hsl(var(--syntax-invalid-deprecated))">]</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Task</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">CreateCustomer_WithValidData_Returns_CreatedResponse</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">using</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">client</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebAppFactory</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateClient</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">createCustomerCommand</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CreateCustomer</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Command</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant-numeric))">FirstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;John&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant-numeric))">LastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Doe&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant-numeric))">BillingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CreateCustomer</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">BillingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant-numeric))">Street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;123 Main St&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant-numeric))">City</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Anytown&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant-numeric))">ZipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;12345&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant-numeric))">ShippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CreateCustomer</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ShippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant-numeric))">Street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;456 Oak Ave&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant-numeric))">City</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Somewhere&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant-numeric))">ZipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;67890&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant-numeric))">Note</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Leave at door&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">response</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">client</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">PostAsJsonAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;/customers&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">createCustomerCommand</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Assert</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">That</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">response</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">StatusCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IsEqualTo</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">HttpStatusCode</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Created</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>While this works, it has its downsides...
As mentioned before, the test is tightly coupled to the API&#39;s internal implementation.</p>
		<h2 id="why-kiota">
		  <a href="#why-kiota" class="anchor mark-hover" tabindex="-1">Why Kiota</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>To make the code within the test strongly typed, a better approach would be to use a generated API client that mirrors the API&#39;s structure and endpoints. This way, you can leverage the benefits of strong typing and avoid hardcoding routes and request structures. There are several tools available to generate API clients from an <a class="mark mark-hover" href="https://spec.openapis.org/oas/latest.html" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fspec.openapis.org)'>OpenAPI specification</a>, such as <a class="mark mark-hover" href="https://github.com/RicoSuter/NSwag" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>NSwag</a> and <a class="mark mark-hover" href="https://learn.microsoft.com/en-us/openapi/kiota/overview?WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'>Kiota</a>. In this post, I&#39;ll focus on Kiota, as I recently discovered it and found it had a better experience compared to other tools.</p><blockquote><span class="icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/></svg></span><p>Kiota is a command line tool for generating an API client to call any OpenAPI-described API you&#39;re interested in. The goal is to eliminate the need to take a dependency on a different API client library for every API that you need to call. Kiota API clients provide a strongly typed experience with all the features you expect from a high quality API SDK, but without having to learn a new library for every HTTP API.</p></blockquote>
		<h3 id="using-kiota-to-generate-the-client">
		  <a href="#using-kiota-to-generate-the-client" class="anchor mark-hover" tabindex="-1">Using Kiota to generate the client</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>To get started with Kiota, you first need to install the Kiota CLI tool. You can do this using the following command:</p><pre id="dea1b34090c4272083e4f0dc9a92e7c7"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="dea1b34090c4272083e4f0dc9a92e7c7" data-status="dea1b34090c4272083e4f0dc9a92e7c7-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="dea1b34090c4272083e4f0dc9a92e7c7-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">tool</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">install</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">--global</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">Kiota</span></div></code></pre><p>Using the <code>kiota</code> command, you can generate a client library from an OpenAPI specification:</p><pre id="f9577699d7b88b452bb0c82e82756e9a"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="f9577699d7b88b452bb0c82e82756e9a" data-status="f9577699d7b88b452bb0c82e82756e9a-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="f9577699d7b88b452bb0c82e82756e9a-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">kiota</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">generate</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">\</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">-l</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">CSharp</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">\</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">--namespace-name</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">ApiServiceSDK</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">\</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">--class-name</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">ApiClient</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">\</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">--openapi</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">./Sandbox.ApiService/obj/Sandbox.ApiService.json</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">\</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">--output</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">../Sandbox.Modules.CustomerManagement.IntegrationTests/ApiServiceSDK</span></div></code></pre><p>The above command generates a C# client library in the specified output directory, using the provided OpenAPI specification file from the API project.
It&#39;s also possible to provide the URL to the API&#39;s OpenAPI endpoint instead of a local file path.
Personally, I prefer the local file approach, as it allows me generate the client without needing to run the API first.</p>
		<h3 id="prerequisite-generating-the-openapi-specification">
		  <a href="#prerequisite-generating-the-openapi-specification" class="anchor mark-hover" tabindex="-1">Prerequisite: Generating the OpenAPI specification</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>To enable OpenAPI support in your ASP.NET Core Web API and generate the OpenAPI specification file, you can use the <code>Microsoft.AspNetCore.OpenApi</code> package (this is added by default starting from .NET 9). Next, configure the OpenAPI middleware in your <code>Program.cs</code> file as shown in the minimal example below:</p><pre id="8361d78cbba5420a7380ecc1e1395f03"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="8361d78cbba5420a7380ecc1e1395f03" data-status="8361d78cbba5420a7380ecc1e1395f03-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="8361d78cbba5420a7380ecc1e1395f03-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">using</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Microsoft</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">AspNetCore</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">OpenApi</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddOpenApi</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Environment</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IsDevelopment</span><span style="color: hsl(var(--syntax-invalid-deprecated))">())</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapOpenApi</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre><p>This configuration activates the OpenAPI middleware, which generates the OpenAPI specification file for your API at run-time.
To also generate the OpenAPI specification file during the build process, you must add the <code>Microsoft.Extensions.ApiDescription.Server</code> package as well.
I also like to set the <a class="mark mark-hover" href="https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props?WT.mc_id=DT-MVP-5004452#generatedocumentationfile" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'><code>GenerateDocumentationFile</code> property</a> in your API project&#39;s <code>.csproj</code> file to include XML comments in the generated OpenAPI specification:</p><pre id="488f4113af7bc29420d0ee53de7779c0"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.77778 6.66667C4.77778 5.19391 5.97169 4 7.44444 4C7.93536 4 8.33333 4.39797 8.33333 4.88889C8.33333 5.3798 7.93536 5.77778 7.44444 5.77778C6.95353 5.77778 6.55556 6.17575 6.55556 6.66667V10.1675C6.55556 10.8682 6.28251 11.5173 5.82622 12C6.28251 12.4827 6.55556 13.1318 6.55556 13.8325V17.3333C6.55556 17.8243 6.95353 18.2222 7.44444 18.2222C7.93536 18.2222 8.33333 18.6202 8.33333 19.1111C8.33333 19.602 7.93536 20 7.44444 20C5.97169 20 4.77778 18.8061 4.77778 17.3333V13.8325C4.77778 13.4246 4.50018 13.0691 4.10448 12.9701L3.6733 12.8623C3.2776 12.7635 3 12.4079 3 12C3 11.5921 3.2776 11.2365 3.6733 11.1377L4.10448 11.0299C4.50018 10.9309 4.77778 10.5754 4.77778 10.1675V6.66667ZM19 6.66667C19 5.19391 17.8061 4 16.3333 4C15.8424 4 15.4444 4.39797 15.4444 4.88889C15.4444 5.3798 15.8424 5.77778 16.3333 5.77778C16.8243 5.77778 17.2222 6.17575 17.2222 6.66667V10.1675C17.2222 10.8682 17.4953 11.5173 17.9516 12C17.4953 12.4827 17.2222 13.1318 17.2222 13.8325V17.3333C17.2222 17.8243 16.8243 18.2222 16.3333 18.2222C15.8424 18.2222 15.4444 18.6202 15.4444 19.1111C15.4444 19.602 15.8424 20 16.3333 20C17.8061 20 19 18.8061 19 17.3333V13.8325C19 13.4246 19.2776 13.0691 19.6733 12.9701L20.1044 12.8623C20.5002 12.7635 20.7778 12.4079 20.7778 12C20.7778 11.5921 20.5002 11.2365 20.1044 11.1377L19.6733 11.0299C19.2776 10.9309 19 10.5754 19 10.1675V6.66667Z" fill="#8B5CF6"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="488f4113af7bc29420d0ee53de7779c0" data-status="488f4113af7bc29420d0ee53de7779c0-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="488f4113af7bc29420d0ee53de7779c0-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="xml dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Project</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">Sdk</span><span style="color: hsl(var(--syntax-meta-tag))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Microsoft.NET.Sdk.Web&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">PropertyGroup</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">GenerateDocumentationFile</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">true</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">GenerateDocumentationFile</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">PropertyGroup</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">ItemGroup</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">PackageReference</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">Include</span><span style="color: hsl(var(--syntax-meta-tag))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Microsoft.AspNetCore.OpenApi&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">PackageReference</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">Include</span><span style="color: hsl(var(--syntax-meta-tag))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Microsoft.Extensions.ApiDescription.Server&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">IncludeAssets</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">runtime; build; native; contentfiles; analyzers; buildtransitive</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">IncludeAssets</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">PrivateAssets</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">all</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">PrivateAssets</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">PackageReference</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">ItemGroup</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">Project</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div></code></pre><p>Now the specification file is automatically generated during the build process, and is created within the <code>obj</code> directory of your project directory (this can be customized using the <a class="mark mark-hover" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/aspnetcore-openapi?WT.mc_id=DT-MVP-5004452#modifying-the-output-directory-of-the-generated-open-api-file" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'><code>OpenApiDocumentsDirectory</code> property</a>). For more information about generating the OpenAPI documents, please take a look at the <a class="mark mark-hover" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/aspnetcore-openapi?WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'>official documentation</a>.</p><p>For an example of a generated OpenAPI specification document, you can check out the file <a class="mark mark-hover" href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.ApiService/documentation/Sandbox.ApiService.json" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>Sandbox.ApiService.json</a>. It contains information about the available endpoints, request and response models, and other relevant details about the API.</p>
		<h3 id="generated-client">
		  <a href="#generated-client" class="anchor mark-hover" tabindex="-1">Generated client</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>With the openAPI specification file in place, you can now generate the API client using Kiota as shown earlier. Once the client is generated, you can use it in your integration tests to interact with the API in a type-safe manner.</p><p>Using the OpenAPI specification, Kiota generates classes and methods that correspond to the API&#39;s endpoints and models, and adds these to your project.
Because we want to use Kiota for testing, the generated client is added to the integration test project.</p><p>The generated client for my sample API can be found at <a class="mark mark-hover" href="https://github.com/timdeschryver/Sandbox/tree/main/Sandbox.Modules.CustomerManagement.IntegrationTests/ApiServiceSDK" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>Sandbox/Sandbox.Modules.CustomerManagement.IntegrationTests
/ApiServiceSDK</a>.</p>
		<h3 id="testcase-with-generated-client">
		  <a href="#testcase-with-generated-client" class="anchor mark-hover" tabindex="-1">Testcase with generated client</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>The refactored integration test using the generated API client looks like this.</p><pre id="e65605879bf189194e01958ea868abe8"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Modules.CustomerManagement.IntegrationTests/CustomerApiTests.cs" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="e65605879bf189194e01958ea868abe8" data-status="e65605879bf189194e01958ea868abe8-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="e65605879bf189194e01958ea868abe8-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-entity-name-section))">Test</span><span style="color: hsl(var(--syntax-invalid-deprecated))">]</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Task</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">CreateCustomer_WithValidData_Returns_CreatedResponse</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">apiClient</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebAppFactory</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateApiClient</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">response</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">apiClient</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Customers</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">PostAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ApiServiceSDK</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Models</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Command</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">FirstName</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;John&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">LastName</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Doe&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">BillingAddress</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ApiServiceSDK</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Models</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Command</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Command_billingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">BillingAddress</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ApiServiceSDK</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Models</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">BillingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">Street</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;123 Main St&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">City</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Anytown&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">ZipCode</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;12345&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">ShippingAddress</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ApiServiceSDK</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Models</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Command</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Command_shippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">ShippingAddress</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ApiServiceSDK</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Models</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ShippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">Street</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;456 Oak Ave&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">City</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Somewhere&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">ZipCode</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;67890&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">Note</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Leave at door&quot;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>To create the <code>ApiClient</code> instance, I added a method to the <code>WebApplicationFactory</code> class, which instantiates the client using the <code>HttpClientRequestAdapter</code> provided by Kiota.</p><pre id="9fc61227bac6b3a130a86748fbd432af"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="9fc61227bac6b3a130a86748fbd432af" data-status="9fc61227bac6b3a130a86748fbd432af-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="9fc61227bac6b3a130a86748fbd432af-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ApiClient</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">CreateApiClient</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">client</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">CreateClient</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">authProvider</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">AnonymousAuthenticationProvider</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">using</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">adapter</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">HttpClientRequestAdapter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">authProvider</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">httpClient</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">client</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ApiClient</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">adapter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre>
		<h3 id="bonus-tip">
		  <a href="#bonus-tip" class="anchor mark-hover" tabindex="-1">Bonus tip</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Instead of manually running the Kiota command each time the API changes, you can automate the client generation process by adding a custom MSBuild target to your integration test project&#39;s <code>.csproj</code> file. This way, the client will be regenerated automatically during the build process.</p><pre id="a38442821c1a1d5a7efc0f9108070552"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.77778 6.66667C4.77778 5.19391 5.97169 4 7.44444 4C7.93536 4 8.33333 4.39797 8.33333 4.88889C8.33333 5.3798 7.93536 5.77778 7.44444 5.77778C6.95353 5.77778 6.55556 6.17575 6.55556 6.66667V10.1675C6.55556 10.8682 6.28251 11.5173 5.82622 12C6.28251 12.4827 6.55556 13.1318 6.55556 13.8325V17.3333C6.55556 17.8243 6.95353 18.2222 7.44444 18.2222C7.93536 18.2222 8.33333 18.6202 8.33333 19.1111C8.33333 19.602 7.93536 20 7.44444 20C5.97169 20 4.77778 18.8061 4.77778 17.3333V13.8325C4.77778 13.4246 4.50018 13.0691 4.10448 12.9701L3.6733 12.8623C3.2776 12.7635 3 12.4079 3 12C3 11.5921 3.2776 11.2365 3.6733 11.1377L4.10448 11.0299C4.50018 10.9309 4.77778 10.5754 4.77778 10.1675V6.66667ZM19 6.66667C19 5.19391 17.8061 4 16.3333 4C15.8424 4 15.4444 4.39797 15.4444 4.88889C15.4444 5.3798 15.8424 5.77778 16.3333 5.77778C16.8243 5.77778 17.2222 6.17575 17.2222 6.66667V10.1675C17.2222 10.8682 17.4953 11.5173 17.9516 12C17.4953 12.4827 17.2222 13.1318 17.2222 13.8325V17.3333C17.2222 17.8243 16.8243 18.2222 16.3333 18.2222C15.8424 18.2222 15.4444 18.6202 15.4444 19.1111C15.4444 19.602 15.8424 20 16.3333 20C17.8061 20 19 18.8061 19 17.3333V13.8325C19 13.4246 19.2776 13.0691 19.6733 12.9701L20.1044 12.8623C20.5002 12.7635 20.7778 12.4079 20.7778 12C20.7778 11.5921 20.5002 11.2365 20.1044 11.1377L19.6733 11.0299C19.2776 10.9309 19 10.5754 19 10.1675V6.66667Z" fill="#8B5CF6"/>
</svg>
 <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Modules.CustomerManagement.IntegrationTests/Sandbox.Modules.CustomerManagement.IntegrationTests.csproj" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="a38442821c1a1d5a7efc0f9108070552" data-status="a38442821c1a1d5a7efc0f9108070552-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="a38442821c1a1d5a7efc0f9108070552-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="xml dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Project</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">Sdk</span><span style="color: hsl(var(--syntax-meta-tag))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Microsoft.NET.Sdk.Web&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Target</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">Name</span><span style="color: hsl(var(--syntax-meta-tag))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;GenerateClient&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">AfterTargets</span><span style="color: hsl(var(--syntax-meta-tag))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Build&quot;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">Condition</span><span style="color: hsl(var(--syntax-meta-tag))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;$(Configuration)=='Debug'&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Exec</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">Command</span><span style="color: hsl(var(--syntax-meta-tag))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;kiota generate -l CSharp --output ./ApiServiceSDK --namespace-name ApiServiceSDK --class-name ApiClient --openapi ../Sandbox.ApiService/documentation/Sandbox.ApiService.json --clean-output&quot;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">WorkingDirectory</span><span style="color: hsl(var(--syntax-meta-tag))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;$(ProjectDir)&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">Target</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">Project</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div></code></pre><p>Keep in mind that if you&#39;re building your application within a CI/CD pipeline, you might need to install the Kiota tool in the pipeline environment first.
That&#39;s why I added the condition to only run the target in Debug mode, so it only runs during local development.</p>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Using the OpenAPI specification document to generate a strongly typed API client with Kiota has significantly improved my integration test flows. It eliminates the need for hardcoded routes and allows me to leverage the generated models, allowing me to quickly update models (even while this is prohibited by the production API models). If you&#39;re looking for a way to streamline your API testing process. While other tools exists, I highly recommend giving Kiota a try because of its ease of use and the way it creates the API methods.</p>
		<h3 id="benefits">
		  <a href="#benefits" class="anchor mark-hover" tabindex="-1">Benefits</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Using Kiota to generate a strongly typed API client from the OpenAPI specification has several benefits for integration testing:</p><ul>
<li><strong>Type safety</strong>: The generated client provides strong typing for API endpoints and models, reducing the risk of runtime errors due to typos or usage of incorrect data types.</li>
<li><strong>Maintainability</strong>: When the API changes, you can simply regenerate the client using Kiota, ensuring that your tests are always in sync with the API&#39;s specification.</li>
<li><strong>Decoupling from production code</strong>: By using the generated client, you can avoid coupling your tests to the internal implementation of the API, allowing for more flexible and isolated testing.</li>
</ul>

		<h3 id="more-than-just-testing">
		  <a href="#more-than-just-testing" class="anchor mark-hover" tabindex="-1">More than just testing</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>In this blog post we&#39;ve seen how Kiota is used for testing purposes, but Kiota can also be used to generate clients for consuming APIs in production code. This can be especially useful when working with third-party APIs that provide OpenAPI specifications. Instead of manually re-creating the client code, you can use Kiota to generate a client that is always in sync with the API&#39;s specification. This makes integrating with external APIs much easier and faster to implement.</p><p>You can find the complete integration test code in my <a class="mark mark-hover" href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Modules.CustomerManagement.IntegrationTests" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>Sandbox project</a>.</p>
		<h2 id="more-resources">
		  <a href="#more-resources" class="anchor mark-hover" tabindex="-1">More resources</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><ul>
<li><a class="mark mark-hover" href="https://www.youtube.com/watch?v=sQ9Pv-rQ1s8" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.youtube.com)'>Introducing project Kiota a client generator for OpenAPI | .NET Conf 2023</a></li>
</ul>
]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Refactoring a form to a Signal Form]]></title>
					<description><![CDATA[I jotted down some notes of my experiences and thoughts while refactoring an existing Angular form to use the new Signal Forms (experimental feature).]]></description>
					<link>https://timdeschryver.dev/blog/refactoring-a-form-to-a-signal-form</link>
					<guid>https://timdeschryver.dev/blog/refactoring-a-form-to-a-signal-form</guid>
					<pubDate>Thu, 16 Oct 2025 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/refactoring-a-form-to-a-signal-form/images/banner.webp" alt="Refactoring a form to a Signal Form"/><p>Read <strong>Refactoring a form to a Signal Form</strong> on <a href="https://timdeschryver.dev/blog/refactoring-a-form-to-a-signal-form">timdeschryver.dev</a></p><p>In this blog post we will refactor an existing Angular form to use the new Signal Forms feature, which will be introduced in Angular 21 as an experimental feature. This won&#39;t be a detailed introduction to Signal Forms, for that I highly recommend looking at the <a class="mark mark-hover" href="#extra-resources" >Extra Resources</a> section at the end of this post, these resources have helped me during the refactoring process.</p><p>Signal forms is a new way to build forms in Angular, it leverages signals to provide a more declarative and efficient way to manage form state and validation.
Compared to the already existing Template Driven Forms and Reactive Forms, Signal Forms leans more towards the Reactive Forms approach, but uses Signals instead of Observables. This change in underlying technology allows for a more seamless integration with Angular&#39;s reactivity model and change detection.
With the addition of Signal Forms, the Angular team took the opportunity to rethink and redesign the forms API.</p><p>The example form is a small form with a first and last name field, both required.
It also consists of two optional address groups (for billing and shipping), which are only shown when the corresponding checkbox is checked.
Putting my best designer hat on, the form looks like this:</p><figure>
				<img src="https://timdeschryver.dev/blog/refactoring-a-form-to-a-signal-form/images/form.webp" alt="" loading="lazy"/>
				<figcaption>Example form with first name, last name, and address fields</figcaption>
			</figure>
		<h2 id="creating-the-form">
		  <a href="#creating-the-form" class="anchor mark-hover" tabindex="-1">Creating the Form</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Because I&#39;m #TeamTemplateDrivenForms, the original form was built using that approach. This means that the form is mostly defined within the HTML template, including the validation rules. Only the model is defined on the TypeScript side.</p><p>Using Signal Forms, the form is defined entirely in the TypeScript code, and the HTML template is only used to bind the form fields to the UI elements.
To create the form, use the <code>form</code> function from the <code>@angular/forms/signals</code> package that takes the form model as an argument (this needs to be a signal).
I like to create a separate method to initialize the form model, which can be reused to reset the form later on.</p><pre id="c61a2aa4be82ce37c38693a2b9ed6baf"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-form.component.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="c61a2aa4be82ce37c38693a2b9ed6baf" data-status="c61a2aa4be82ce37c38693a2b9ed6baf-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="c61a2aa4be82ce37c38693a2b9ed6baf-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">@</span><span style="color: hsl(var(--syntax-constant-numeric))">Component</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))">...</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerForm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">protected</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">readonly</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">customerForm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">signal</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">initializeCustomerModel</span><span style="color: hsl(var(--syntax-meta-tag))">()))</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">private</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">initializeCustomerModel</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerFormModel</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      hasBillingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      billingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      hasShippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      shippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        note</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>Within the HTML template, the model properties are bound to form fields using the <code>field</code> directive. This requires us to import the <code>Field</code> directive from <code>@angular/forms/signals</code> and add it to the <code>imports</code> array of the component decorator.</p><p>This translates to the following HTML:</p>
        <div class="code-group">
			<div class="code-group-tabs" role="tablist" aria-label="Code examples"> <button type="button" id="code-tab-145" data-id="145" class="code-group-tab active" role="tab" aria-selected="true" aria-controls="code-panel-145" tabindex="0">Template</button><button type="button" id="code-tab-146" data-id="146" class="code-group-tab " role="tab" aria-selected="false" aria-controls="code-panel-146" tabindex="-1">Component</button></div>
			<div id="code-panel-145" data-id="145" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-145" ><pre id="aa1436cd6c9f678efb3c655d20e5e343"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-form.component.html</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.html" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="aa1436cd6c9f678efb3c655d20e5e343" data-status="aa1436cd6c9f678efb3c655d20e5e343-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="aa1436cd6c9f678efb3c655d20e5e343-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="html dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">form</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">novalidate</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">fieldset</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">legend</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Customer Information</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">legend</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">for</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;firstName&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">First Name</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">id</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;firstName&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;text&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[field]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;customerForm.firstName&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">for</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;lastName&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Last Name</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">id</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;lastName&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;text&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[field]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;customerForm.lastName&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">fieldset</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">form</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div></code></pre></div><div id="code-panel-146" data-id="146" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-146" hidden><pre id="b50f3da523c8e2b1467e6a6ae82626e0"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">customer-form.component.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="b50f3da523c8e2b1467e6a6ae82626e0" data-status="b50f3da523c8e2b1467e6a6ae82626e0-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="b50f3da523c8e2b1467e6a6ae82626e0-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">@</span><span style="color: hsl(var(--syntax-constant-numeric))">Component</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">  imports</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span><span style="color: hsl(var(--syntax-meta-tag))">Field</span><span style="color: hsl(var(--syntax-meta-tag))">]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerForm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">protected</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">readonly</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">customerForm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">signal</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">initializeCustomerModel</span><span style="color: hsl(var(--syntax-meta-tag))">()))</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">private</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">initializeCustomerModel</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerFormModel</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      hasBillingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      billingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      hasShippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      shippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        note</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre></div>
		</div>
		<h2 id="validation">
		  <a href="#validation" class="anchor mark-hover" tabindex="-1">Validation</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Of course, a form isn&#39;t very useful without validation.
With Signal Forms, validation rules are defined within the second argument of the <code>form</code> function.</p><p>This is a callback function that provides a <code>path</code> parameter that is the root level of the given form model.
Via the <code>path</code> variable you can access all the form fields and groups to define validation rules.</p><p>The rules themselves are similar to the ones used in Reactive Forms, but are standalone functions that are imported from the <code>@angular/forms/signals</code> package.
In the example below, we only use the <code>required</code> and <code>maxLength</code> validators, but there are more built-in validators available, and you can also create custom validators. At the time of writing this, the built-in validators are: <code>required</code>, <code>minLength</code>, <code>maxLength</code>, <code>min</code>, <code>max</code>, <code>pattern</code>, and <code>email</code>.</p><pre id="3b0640bd398e536862b3ff14e841b171"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-form.component.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="3b0640bd398e536862b3ff14e841b171" data-status="3b0640bd398e536862b3ff14e841b171-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="3b0640bd398e536862b3ff14e841b171-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">@</span><span style="color: hsl(var(--syntax-constant-numeric))">Component</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))">...</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerForm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">protected</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">readonly</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">customerForm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">signal</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">initializeCustomerModel</span><span style="color: hsl(var(--syntax-meta-tag))">())</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">path</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Can also set a specific error message</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> message</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'First name is required'</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">maxLength</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">255</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">lastName</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Can also set a dynamic max length</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">maxLength</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">lastName</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">value</span><span style="color: hsl(var(--syntax-meta-tag))">() </span><span style="color: hsl(var(--syntax-constant))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">255</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">0</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">private</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">initializeCustomerModel</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerFormModel</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      hasBillingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      billingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      hasShippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      shippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        note</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>The validation is enabled by default, but it&#39;s possible to add a condition to only enable the validation when certain criteria are met. An example follows in the <a class="mark mark-hover" href="#nested-groups" >Nested Groups</a> section.</p><p>The errors are exposed on each field via the <code>errors</code> property, which is a signal that emits the current validation errors (if there are no errors the collection is empty). The error is expressed as a <a class="mark mark-hover" href="https://github.com/angular/angular/blob/main/packages/forms/signals/src/api/validation_errors.ts#L309" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'><code>ValidationErrors</code> object</a> that contains a <code>kind</code> (identifies the kind of error), the <code>field</code> (the field associated with this error), and an optional <code>message</code> (human readable error message) property.</p><pre id="82d5427cc9858ba2bbe9ff073d0b827d"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">validation_errors.ts</span> <a href="https://github.com/angular/angular/blob/main/packages/forms/signals/src/api/validation_errors.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="82d5427cc9858ba2bbe9ff073d0b827d" data-status="82d5427cc9858ba2bbe9ff073d0b827d-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="82d5427cc9858ba2bbe9ff073d0b827d-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">interface</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ValidationError</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">/**</span><span style="color: hsl(var(--syntax-comment))"> Identifies the kind of error. </span><span style="color: hsl(var(--syntax-invalid-deprecated))">*/</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">readonly</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">kind</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">/**</span><span style="color: hsl(var(--syntax-comment))"> The field associated with this error. </span><span style="color: hsl(var(--syntax-invalid-deprecated))">*/</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">readonly</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">field</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">FieldTree</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">unknown</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">/**</span><span style="color: hsl(var(--syntax-comment))"> Human readable error message. </span><span style="color: hsl(var(--syntax-invalid-deprecated))">*/</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">readonly</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">message</span><span style="color: hsl(var(--syntax-constant))">?:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>To display the errors in the UI, we can loop over the <code>errors</code> collection and display each error message.
To keep it simple, this example just displays the JSON representation of the error object.
In a real-world application, I would recommend creating a dedicated component to display the errors in a more user-friendly way, or use a directive to dynamically for each field like I did in my <a class="mark mark-hover" href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/form-validation-lib/src/lib/error.ts" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>Sandbox application</a>.</p><pre id="ecd5e1afad63598c90808065ad842a40"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-form.component.html</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.html" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="ecd5e1afad63598c90808065ad842a40" data-status="ecd5e1afad63598c90808065ad842a40-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="ecd5e1afad63598c90808065ad842a40-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="angular-html dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">for</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;firstName&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">First Name</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">id</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;firstName&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;text&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[field]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;customerForm.firstName&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">@for</span><span style="color: hsl(var(--syntax-meta-tag))"> (</span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">of</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">customerForm</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">firstName</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">errors</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">track</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">$index</span><span style="color: hsl(var(--syntax-meta-tag))">) </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">|</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">json</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}}</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div></code></pre><p>To check the validatity of a field, use the <code>valid</code>, <code>invalid</code>, and <code>pending</code> (useful for async validation) signals.
There is no <code>status</code> property to get the current state as a string.</p>
		<h3 id="differences">
		  <a href="#differences" class="anchor mark-hover" tabindex="-1">Differences</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>At runtime, there are some differences compared to the existing form implementations:</p><ul>
<li>The validators are added to the input element as attributes, e.g. <code>required</code> and <code>maxlength</code>.</li>
<li>The name attribute is automatically generated based on the form field&#39;s path within the form model. This ensures that each form field has a unique name, which is important for form submission and accessibility.</li>
<li>There are no classes added to the input element to indicate the validation state (<code>ng-valid</code> and <code>ng-invalid</code>, <code>ng-touched</code>, etc.).</li>
</ul>
<pre id="f79005dd28892880dbad7ff1cd6fdf8c"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">generated-output.html</span> <button type="button" class="copy-code icon align-text-top" data-ref="f79005dd28892880dbad7ff1cd6fdf8c" data-status="f79005dd28892880dbad7ff1cd6fdf8c-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="f79005dd28892880dbad7ff1cd6fdf8c-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="html " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">id</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;firstName&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;text&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">name</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;ng.form0.firstName&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">required</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">maxlength</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;255&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div></code></pre>
		<h3 id="field-states">
		  <a href="#field-states" class="anchor mark-hover" tabindex="-1">Field states</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Each form field has a set of signals to track its state, such as <code>touched</code> (there is no <code>untouched</code> counterpart), <code>dirty</code> (there is no <code>pristine</code> counterpart), <code>disabled</code>, <code>readonly</code>, and <code>hidden</code>. Just as we&#39;re used to, the form field updates the <code>touched</code> and <code>dirty</code> states automatically based on user interaction. There are also the <code>markAsTouched()</code> and <code>markAsDirty()</code> methods to update the state programmatically.</p><p>Similar to how we define the validation rules, the conditions for the <code>disabled</code>, <code>readonly</code>, and <code>hidden</code> states are be defined within the second argument of the <code>form</code> function.</p><pre id="cfcfc35d101c81c1b7e94ce6beca41a6"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-form.component.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="cfcfc35d101c81c1b7e94ce6beca41a6" data-status="cfcfc35d101c81c1b7e94ce6beca41a6-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="cfcfc35d101c81c1b7e94ce6beca41a6-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">@</span><span style="color: hsl(var(--syntax-constant-numeric))">Component</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))">...</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerForm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">protected</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">readonly</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">customerForm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">signal</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">initializeCustomerModel</span><span style="color: hsl(var(--syntax-meta-tag))">())</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">path</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">firstName</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">maxLength</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">255</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">lastName</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">maxLength</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">255</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Always</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">disabled</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">firstName</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Based on a condition</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">readonly</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">lastName</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">value</span><span style="color: hsl(var(--syntax-meta-tag))">() </span><span style="color: hsl(var(--syntax-constant))">===</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Tim'</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-invalid-deprecated))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Based on a different property in the form</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">hidden</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">shippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">logic</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">logic</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">valueOf</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">hasShippingAddress</span><span style="color: hsl(var(--syntax-meta-tag))">))</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">private</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">initializeCustomerModel</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerFormModel</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      hasBillingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      billingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      hasShippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      shippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        note</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre>
		<h3 id="differences-2">
		  <a href="#differences-2" class="anchor mark-hover" tabindex="-1">Differences</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Just as with validation, these states affect the input element at runtime:</p><ul>
<li>The <code>disabled</code> and <code>readonly</code> states are reflected as attributes on the input element. If these conditions are set to a parent group, they are applied to all child fields. The <code>hidden</code> state does not affect the input element, it&#39;s up to you to hide the element in the UI based on this state</li>
<li>Again, there are no classes added to the input element to indicate the state (<code>ng-touched</code>, <code>ng-dirty</code>, <code>ng-pristine</code>, etc.).</li>
</ul>

		<h2 id="nested-groups">
		  <a href="#nested-groups" class="anchor mark-hover" tabindex="-1">Nested groups</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The example form contains two nested groups for the billing and shipping address.
Both have the same structure (city, street, and zip code), but the shipping address has an additional note field.
Instead of repeating the same code twice, we can create some reusable components for the address fields.</p><p>The first part is that we can use the <code>schema</code> function to define the structure and validation rules for an address. This function takes a callback that provides a <code>path</code> parameter, similar to the one used in the <code>form</code> function.</p><pre id="3a4d8b26bef254016d1038d476aba904"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-form.component.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="3a4d8b26bef254016d1038d476aba904" data-status="3a4d8b26bef254016d1038d476aba904-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="3a4d8b26bef254016d1038d476aba904-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">addressSchema</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">schema</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Address</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">path</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">street</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">minLength</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">3</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">city</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">minLength</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">3</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">zipCode</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">minLength</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">3</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> message</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Zip code must be between 3 and 5 characters long.'</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">maxLength</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">5</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> message</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Zip code must be between 3 and 5 characters long.'</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>Next, we can apply this schema to both address groups in the main form definition.
Because the billing and shipping address are only applicable when the corresponding checkbox is checked, the <code>address</code> <code>schema</code> is only applied when the checkbox is checkbox. In code we use the <code>applyWhen</code> function for this, if the schema can always be applied, use the <code>apply</code> function instead.</p><pre id="3df6e6a9350b886ab1d251d3b61343da"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-form.component.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="3df6e6a9350b886ab1d251d3b61343da" data-status="3df6e6a9350b886ab1d251d3b61343da-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="3df6e6a9350b886ab1d251d3b61343da-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">@</span><span style="color: hsl(var(--syntax-constant-numeric))">Component</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))">...</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerForm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">protected</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">readonly</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">customerForm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">signal</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">initializeCustomerModel</span><span style="color: hsl(var(--syntax-meta-tag))">())</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">path</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">firstName</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">maxLength</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">255</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">lastName</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">maxLength</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">255</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">hidden</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">billingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">logic</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">logic</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">valueOf</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">hasBillingAddress</span><span style="color: hsl(var(--syntax-meta-tag))">))</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">applyWhen</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">billingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">logic</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">logic</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">valueOf</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">hasBillingAddress</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">addressSchema</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">hidden</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">shippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">logic</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">logic</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">valueOf</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">hasShippingAddress</span><span style="color: hsl(var(--syntax-meta-tag))">))</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">applyWhen</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">shippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">logic</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">logic</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">valueOf</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">hasShippingAddress</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">addressSchema</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">minLength</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">shippingAddress</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">note</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">logic</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">logic</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">value</span><span style="color: hsl(var(--syntax-meta-tag))">() </span><span style="color: hsl(var(--syntax-constant))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">10</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">0</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">private</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">initializeCustomerModel</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerFormModel</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      hasBillingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      billingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      hasShippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      shippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        note</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">''</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>In the example above you can also see the usage of the <code>hidden</code> function to hide the address groups when the corresponding checkbox is not checked.
This makes it easy to manage the visibility of the groups based on the form state, instead of adding custom logic in the HTML template.</p><p>Lastly, we can create a reusable component for the address fields that takes an address as input.
While defining the input, the <code>Property</code> type is used. To be honest, I don&#39;t know if this is the best approach, but I like the simplicity of it.</p><p>The HTML of the address sub form is identical to the fields of the main form.</p>
        <div class="code-group">
			<div class="code-group-tabs" role="tablist" aria-label="Code examples"> <button type="button" id="code-tab-147" data-id="147" class="code-group-tab active" role="tab" aria-selected="true" aria-controls="code-panel-147" tabindex="0">Component</button><button type="button" id="code-tab-148" data-id="148" class="code-group-tab " role="tab" aria-selected="false" aria-controls="code-panel-148" tabindex="-1">Template</button></div>
			<div id="code-panel-147" data-id="147" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-147" ><pre id="7d8c912c5c2805a8284ca93757856ec4"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-address.component.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/shared/customer-address/customer-address.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="7d8c912c5c2805a8284ca93757856ec4" data-status="7d8c912c5c2805a8284ca93757856ec4-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="7d8c912c5c2805a8284ca93757856ec4-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">@</span><span style="color: hsl(var(--syntax-constant-numeric))">Component</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))">...</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerAddress</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">readonly</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">address</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">input</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Property</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">Address</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre></div><div id="code-panel-148" data-id="148" class="code-group-code" role="tabpanel" aria-labelledby="code-tab-148" hidden><pre id="f49754bad55883f24f2d993e7c334e55"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-address.component.html</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/shared/customer-address/customer-address.html" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="f49754bad55883f24f2d993e7c334e55" data-status="f49754bad55883f24f2d993e7c334e55-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="f49754bad55883f24f2d993e7c334e55-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="html " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Street</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;text&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[field]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;address().street&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    City</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;text&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[field]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;address().city&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Zip Code</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;text&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[field]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;address().zipCode&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">ng-content</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div></code></pre></div>
		</div><p>The main form HTML now uses this component for both address groups, passing the corresponding address as input.
These address groups are only shown when the corresponding checkbox is checked, based on the <code>hidden</code> state of the group.</p><pre id="39ca49bd3fc26586c92207f21bb125f1"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-form.component.html</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.html" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="39ca49bd3fc26586c92207f21bb125f1" data-status="39ca49bd3fc26586c92207f21bb125f1-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="39ca49bd3fc26586c92207f21bb125f1-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="angular-html dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">form</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">novalidate</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">h2</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Create Customer</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">h2</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">fieldset</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">legend</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Customer Information</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">legend</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">for</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;firstName&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">First Name</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;text&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[field]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;customerForm.firstName&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">for</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;lastName&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Last Name</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">id</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;lastName&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;text&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[field]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;customerForm.lastName&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">fieldset</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;checkbox&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">id</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;hasBillingAddress&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[field]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;customerForm.hasBillingAddress&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">for</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;hasBillingAddress&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Add Billing Address</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">@if</span><span style="color: hsl(var(--syntax-meta-tag))"> (</span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">customerForm</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">billingAddress</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">hidden</span><span style="color: hsl(var(--syntax-meta-tag))">()) </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">fieldset</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">legend</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Billing Address</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">legend</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">sandbox-customer-address</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[address]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;customerForm.billingAddress&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">fieldset</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;checkbox&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">id</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;hasShippingAddress&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[field]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;customerForm.hasShippingAddress&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-comment))">/&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">for</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;hasShippingAddress&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Add Shipping Address</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">@if</span><span style="color: hsl(var(--syntax-meta-tag))"> (</span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">customerForm</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">shippingAddress</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">hidden</span><span style="color: hsl(var(--syntax-meta-tag))">()) </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">fieldset</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">legend</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Shipping Address</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">legend</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">sandbox-customer-address</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[address]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;customerForm.shippingAddress&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">for</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;note&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Note</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">label</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">textarea</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">id</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;note&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[field]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;customerForm.shippingAddress.note&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">textarea</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">div</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">sandbox-customer-address</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">fieldset</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">form</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div></code></pre><p>The <code>note</code> property also shows that we can traverse into nested groups to bind fields to the UI elements.</p>
		<h2 id="submitting-the-form">
		  <a href="#submitting-the-form" class="anchor mark-hover" tabindex="-1">Submitting the Form</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>There are also some changes regarding form submission.
The form submission is handled by listening to the <code>submit</code> event of the form element, and passes the click event to the handler method.
In the following example you can also notice that the <code>submitting</code> signal is used to disable the submit button while the form is being submitted.</p><pre id="cc9474e5f6e13685a6f2f5e190928057"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-form.component.html</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.html" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="cc9474e5f6e13685a6f2f5e190928057" data-status="cc9474e5f6e13685a6f2f5e190928057-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="cc9474e5f6e13685a6f2f5e190928057-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="angular-html " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">form</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">novalidate</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">(submit)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;submmitCustomerForm($event)&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">button</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;submit&quot;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">[disabled]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">=</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;customerForm().submitting()&quot;</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">@if</span><span style="color: hsl(var(--syntax-meta-tag))"> (</span><span style="color: hsl(var(--syntax-constant-numeric))">customerForm</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">submitting</span><span style="color: hsl(var(--syntax-meta-tag))">()) </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      Creating Customer</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">@else</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      Create Customer</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">button</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">form</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div></code></pre><p>The <code>submitting</code> signal is managed by Angular. To trigger the submit action, the <code>submit</code> function from the <code>@angular/forms/signals</code> package is used. This function takes the form signal and a callback that contains the logic to execute the form submission. Within the callback, the form is passed as an argument.</p><p>Important to note is that the callback is only invoked when the form is valid. If the form is invalid, the callback is not invoked, and the <code>submitting</code> signal remains <code>false</code>.</p><p>Before invoking the callback, the <code>submit</code> method sets the <code>submitting</code> signal to <code>true</code>, and once the callback completes (either successfully or with an error), it resets the <code>submitting</code> signal back to <code>false</code>.
It also marks all fields, including the form itself, as touched and dirty.</p><p>Because the <code>submit</code> function is a <code>Promise</code>, we must <code>await</code> it.</p><pre id="92f9c51d3df62e1d88d88ba43dca0573"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-form.component.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="92f9c51d3df62e1d88d88ba43dca0573" data-status="92f9c51d3df62e1d88d88ba43dca0573-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="92f9c51d3df62e1d88d88ba43dca0573-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">@</span><span style="color: hsl(var(--syntax-constant-numeric))">Component</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))">...</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">CustomerForm</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">protected</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">submmitCustomerForm</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">event</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Event</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Promise</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">void</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">event</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">preventDefault</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">submit</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">customerForm</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">form</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">value</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">firstValueFrom</span><span style="color: hsl(var(--syntax-meta-tag))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">customersService</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">createCustomer</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">lastName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Extra check to clear previously filled fields when checkbox is unchecked</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            billingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">hasBillingAddress</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                  city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">billingAddress</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                  street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">billingAddress</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                  zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">billingAddress</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">null</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> Extra check to clear previously filled fields when checkbox is unchecked</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            shippingAddress</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">hasShippingAddress</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                  city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">shippingAddress</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">city</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                  street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">shippingAddress</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">street</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                  zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">shippingAddress</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">zipCode</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                  note</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">shippingAddress</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">note</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">formValue</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">shippingAddress</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">note</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">null</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">null</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">pipe</span><span style="color: hsl(var(--syntax-meta-tag))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant-numeric))">map</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">reset</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant-numeric))">form</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">value</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">set</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">initializeCustomerModel</span><span style="color: hsl(var(--syntax-meta-tag))">())</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">null</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant-numeric))">catchError</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">error</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">unknown</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Observable</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">OneOrMany</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">WithOptionalField</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">ValidationError</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;&gt;&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">of</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                kind</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'server'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                message</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                  </span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">instanceof</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">HttpErrorResponse</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                    </span><span style="color: hsl(var(--syntax-constant))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">title</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                    </span><span style="color: hsl(var(--syntax-constant))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'An unexpected error occurred, please try again.'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">              </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">          )</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      )</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>In the previous example, the form data is sent to a backend service using an <code>HttpClient</code> call.
Because the <code>submit</code> is asynchronous, and the HTTP call returns an <code>Observable</code>, we must convert it to a <code>Promise</code>. To do this, I used the <code>firstValueFrom</code> function from <code>rxjs</code>, which resolves the <code>Promise</code> with the first emitted value from the <code>Observable</code>.</p><p>If the HTTP call is successful, the form is reset to its initial state using the <code>reset()</code> method, this resets all field states (touched, dirty, validations) of the form.
However, it does not reset the form value, so it&#39;s value is reset manually by calling the <code>set</code> method of the <code>value</code> signal.</p><p>If the HTTP call fails, the error is caught using the <code>catchError</code> operator from <code>rxjs</code>, and a server-side validation error is returned. This error is automatically added to the form&#39;s <code>errors</code> collection, and can be displayed in the UI.</p><p>The form itself also has a collection of its own <code>errors</code>. Which can be useful, but more useful is the ability to access all the errors including those its child fields. With the new signal forms, we can access these errors directly via the <code>errorSummary</code> signal, which is also available on each field and group.
Just as with the errors of a field, the summary is a collection of <code>ValidationError</code> objects, which can easily be traversed and displayed within the UI.</p><pre id="1e5ec49b2acb44cff32459e53cc33c8f"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <span class="file-name">customer-form.component.html</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.html" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="1e5ec49b2acb44cff32459e53cc33c8f" data-status="1e5ec49b2acb44cff32459e53cc33c8f-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="1e5ec49b2acb44cff32459e53cc33c8f-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="angular-html " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">@if</span><span style="color: hsl(var(--syntax-meta-tag))"> (</span><span style="color: hsl(var(--syntax-constant-numeric))">customerForm</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">touched</span><span style="color: hsl(var(--syntax-meta-tag))">() </span><span style="color: hsl(var(--syntax-constant))">&amp;&amp;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">customerForm</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">invalid</span><span style="color: hsl(var(--syntax-meta-tag))">()) </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">h3</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">Validation summary:</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">h3</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">ul</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">@for</span><span style="color: hsl(var(--syntax-meta-tag))"> (</span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">of</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">customerForm</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">errorSummary</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">track</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">$index</span><span style="color: hsl(var(--syntax-meta-tag))">) </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">      </span><span style="color: hsl(var(--syntax-comment))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">li</span><span style="color: hsl(var(--syntax-comment))">&gt;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">error</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">|</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">json</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}}</span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">li</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-comment))">&lt;/</span><span style="color: hsl(var(--syntax-entity-name-section))">ul</span><span style="color: hsl(var(--syntax-comment))">&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre>
		<h3 id="differences-3">
		  <a href="#differences-3" class="anchor mark-hover" tabindex="-1">Differences</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>If you have payed close attention to the <code>submit</code> snippets you might have spotted the <a class="mark mark-hover" href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fdeveloper.mozilla.org)'><code>novalidate</code> attribute</a> on the form element, as well as the <code>event.preventDefault()</code> call in the submit handler.</p><p>This is done to prevent the browser&#39;s built-in validation from interfering with Angular&#39;s validation. Because the validators are added as attributes to the input elements, the browser automatically validate the form before it can submit, which is not desired in this case. With template driven forms or reactive forms, Angular prevents this automatically.</p>
		<h2 id="additional-notes">
		  <a href="#additional-notes" class="anchor mark-hover" tabindex="-1">Additional notes</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Some extra thoughts while I was looking at the documentation and playing around with Signal Forms, but didn&#39;t fit in the previous sections.</p><ul>
<li>In the form model you can see that every field is initialized with a default value, and not with <code>null</code> or <code>undefined</code>. This is is done on purpose, because otherwise the signal definitions are harder to type correctly. It can also act finicky, while binding it to a field. This keeps accessing the field values simple and type-safe.</li>
<li>Instead of creating a custom validator, it&#39;s possible to use the <code>error</code> function to define a custom validation rule directly within the form definition. This can be useful in scenarios where the validation logic is simple and doesn&#39;t need to be reused elsewhere.</li>
</ul>
<pre id="4a5a3cd6ed045122e3ee783171cc8d63"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.23446 9.09304V7.31818H11.5967V9.09304H8.47949V17.5H6.35165V9.09304H3.23446ZM18.4972 10.2464C18.4575 9.84541 18.2868 9.53385 17.9852 9.31179C17.6836 9.08972 17.2742 8.97869 16.7572 8.97869C16.4059 8.97869 16.1092 9.02841 15.8673 9.12784C15.6253 9.22396 15.4397 9.35819 15.3105 9.53054C15.1845 9.70289 15.1215 9.89844 15.1215 10.1172C15.1149 10.2995 15.153 10.4586 15.2359 10.5945C15.3221 10.7304 15.4397 10.848 15.5889 10.9474C15.738 11.0436 15.9104 11.1281 16.1059 11.201C16.3015 11.2706 16.5103 11.3303 16.7323 11.38L17.6471 11.5987C18.0912 11.6982 18.4989 11.8307 18.8701 11.9964C19.2413 12.1622 19.5628 12.366 19.8346 12.608C20.1064 12.8499 20.3169 13.1349 20.466 13.4631C20.6185 13.7912 20.6963 14.1674 20.6997 14.5916C20.6963 15.2147 20.5373 15.755 20.2224 16.2124C19.9108 16.6664 19.4601 17.0194 18.8701 17.2713C18.2835 17.5199 17.5758 17.6442 16.7472 17.6442C15.9253 17.6442 15.2094 17.5182 14.5995 17.2663C13.993 17.0144 13.519 16.6416 13.1776 16.1477C12.8396 15.6506 12.6623 15.0357 12.6457 14.3033H14.7288C14.752 14.6446 14.8498 14.9297 15.0221 15.1584C15.1978 15.3838 15.4314 15.5545 15.7231 15.6705C16.0181 15.7831 16.3512 15.8395 16.7224 15.8395C17.087 15.8395 17.4035 15.7865 17.672 15.6804C17.9437 15.5743 18.1542 15.4268 18.3034 15.2379C18.4525 15.049 18.5271 14.8319 18.5271 14.5866C18.5271 14.358 18.4591 14.1657 18.3232 14.0099C18.1907 13.8542 17.9951 13.7216 17.7366 13.6122C17.4814 13.5028 17.1682 13.4034 16.797 13.3139L15.6883 13.0355C14.8299 12.8267 14.1521 12.5002 13.6549 12.0561C13.1578 11.612 12.9108 11.0137 12.9142 10.2614C12.9108 9.64489 13.0749 9.1063 13.4063 8.6456C13.7411 8.1849 14.2001 7.82528 14.7835 7.56676C15.3668 7.30824 16.0297 7.17898 16.7721 7.17898C17.5278 7.17898 18.1874 7.30824 18.7508 7.56676C19.3176 7.82528 19.7584 8.1849 20.0732 8.6456C20.3881 9.1063 20.5505 9.63991 20.5605 10.2464H18.4972Z" fill="#2563EB"/>
</svg>
 <span class="file-name">customer-form.component.ts</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.AngularWorkspace/projects/sandbox-app/src/app/customer-management/customer-form/customer-form.ts" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="4a5a3cd6ed045122e3ee783171cc8d63" data-status="4a5a3cd6ed045122e3ee783171cc8d63-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="4a5a3cd6ed045122e3ee783171cc8d63-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="ts " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">error</span><span style="color: hsl(var(--syntax-meta-tag))">(</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-meta-tag))">customerForm</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">firstName</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">({</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-other-attribute-name))">value</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-property-value-css))">/</span><span style="color: hsl(var(--syntax-constant))">^</span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-constant))">a-z</span><span style="color: hsl(var(--syntax-invalid-deprecated))">]</span><span style="color: hsl(var(--syntax-constant))">+$</span><span style="color: hsl(var(--syntax-meta-property-value-css))">/</span><span style="color: hsl(var(--syntax-constant))">i</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">test</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">value</span><span style="color: hsl(var(--syntax-meta-tag))">())</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Alphabet characters only'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><ul>
<li>Each field has a unique name that is automatically generated based on its path within the form model. This is a huge improvement for accessibility concerns, as it can serve to bind elements like labels and error messages to the corresponding form fields.</li>
<li>It seems simpler to create cross-field validation rules and async validation rules, for more info see Manfred Steyer&#39;s blog post in the <a class="mark mark-hover" href="#extra-resources" >Extra Resources</a> section.</li>
</ul>

		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Signal Forms aims to simplify the API and improve performance by reducing the amount of boilerplate code needed to manage the form.
By fiddling around using this small form that contains some often-used logic, I can see the potential of Signal Forms to become my go-to approach for building forms in Angular applications. While it&#39;s still in an experimental stage, it does already contains many features to build complex forms. While it can and will evolve over time, I would already consider it for applications that are not used in production environments.</p><p>One of the main reasons I like the new API is the conditional logic that can be applied to validation rules and field states. This makes it easy to manage complex forms with dynamic behavior. This was the main reason I was #TeamTemplateDrivenForms.</p><p>I also like that the form definition is now entirely in the TypeScript code, which makes it easier to reason about. The whole form is now defined in one place, instead of being split between the HTML template and the TypeScript code. I can also see that this can make it easier to test the form logic, as you&#39;re not required to render the HTML template to test the form (allthough I don&#39;t do this, and I also don&#39;t recommend it, see <a class="mark mark-hover" href="https://timdeschryver.dev/blog?q=angular+testing+library" data-sveltekit-reload >Angular Testing Library</a> for the reasons behind it).</p><p>The benefit it has over Reactive Forms is that it&#39;s now built on top of Signals, which are more efficient than Observables for this use case as it needs to react on state changes (and not on events).</p><p>Because I test my forms from a user&#39;s perspective, this change to Signal Forms didn&#39;t affect my testing approach. The removal of the <code>ng-</code> classes did not affect my tests. On the other hand, it makes it a harder to style the form based on its state. Maybe that this is a feature that will be added in the future.</p><p>There&#39;s one point that I don&#39;t like.
I&#39;m not a big fan of the validation attributes being added to the input elements. For example, the <code>maxlength</code> attribute is added to the input element, which will cut off the input when the user reaches the maximum length. This can be frustrating for users, especially when they paste a long text and don&#39;t notice that it has been cut off. I would prefer to have more control over this behavior, for example by providing an option to disable this feature. Maybe that this already exists, but I haven&#39;t come across it yet.</p><p>This makes me now in the camp of #TeamSignalForms.</p><p>The code used within this blog post can be accessed in <a class="mark mark-hover" href="https://github.com/timdeschryver/Sandbox/tree/main/Sandbox.AngularWorkspace" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>repository</a> (this will continue to change with the new versions), or you can also check out the <a class="mark mark-hover" href="https://github.com/timdeschryver/Sandbox/pull/54/files#diff-e9281f4cfd6b0708e39b676062896d0f20ca1615645fd00faf3c7b04ff99734a" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>commit in which I refactored</a> the form to use Signal Forms.</p>
		<h2 id="extra-resources">
		  <a href="#extra-resources" class="anchor mark-hover" tabindex="-1">Extra Resources</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><ul>
<li><a class="mark mark-hover" href="https://www.angulararchitects.io/blog/all-about-angulars-new-signal-forms/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.angulararchitects.io)'>All About Angular’s New Signal Forms (blogpost)</a>, by Manfred Steyer</li>
<li><a class="mark mark-hover" href="https://www.youtube.com/watch?v=gvL_mTQZIYE" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fwww.youtube.com)'>Signal Forms Custom Fields — Simplifying FieldValueAccessor (YouTube)</a>, by Dmytro Mezhenskyi</li>
</ul>
]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Using the new bindings API to test Angular components with Angular Testing Library]]></title>
					<description><![CDATA[Learn how to make use of the new binding methods inputBinding, outputBinding, and twoWayBinding to set component properties in your Angular tests. This native integration provides better signal support and eliminates type assertions for output events.]]></description>
					<link>https://timdeschryver.dev/blog/using-the-new-bindings-api-to-test-angular-components-with-angular-testing-library</link>
					<guid>https://timdeschryver.dev/blog/using-the-new-bindings-api-to-test-angular-components-with-angular-testing-library</guid>
					<pubDate>Thu, 02 Oct 2025 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/using-the-new-bindings-api-to-test-angular-components-with-angular-testing-library/images/banner.webp" alt="Using the new bindings API to test Angular components with Angular Testing Library"/><p><a href="https://timdeschryver.dev/blog/using-the-new-bindings-api-to-test-angular-components-with-angular-testing-library?tldr=true">Read the <strong>code-only version</strong> on timdeschryver.dev</a></p><p>Angular Testing Library v18.1.0 adds support for the new <code>bindings</code> functionality in Angular (thank you <a class="mark mark-hover" href="https://github.com/lacolaco" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>Suguru Inatomi (@lacolaco)</a>!).
Using the methods <a class="mark mark-hover" href="https://angular.dev/api/core/inputBinding" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'><code>inputBinding</code></a>, <a class="mark mark-hover" href="https://angular.dev/api/core/outputBinding" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'><code>outputBinding</code></a>, and <a class="mark mark-hover" href="https://angular.dev/api/core/twoWayBinding" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'><code>twoWayBinding</code></a> you can now set the component&#39;s input and output properties using the official tooling (Angular v20.1).</p><p>For those who are currently using Angular Testing Library, you already know that it currently has multiple ways to set the component&#39;s properties through <code>componentProperties</code>, <code>componentInputs</code> (deprecated), <code>inputs</code>, <code>componentOutputs</code> (deprecated), and <code>on</code>. These methods were needed to easily set the input and output properties of a component in tests, and evolved over time to support typesafety and signals.
With the addition of the new built-in methods in Angular, these use cases are now covered by the Angular framework itself.
This will have an impact on the Angular Testing Library API, as the existing methods will be deprecated in future releases in favor of the new <code>bindings</code> property.</p><p>Let&#39;s take a look at the new <code>bindings</code> property in combination with Angular Testing Library, we&#39;ll also cover some cases with just Angular&#39;s functionality without bringing in Angular Testing Library.</p>
		<h2 id="example-component">
		  <a href="#example-component" class="anchor mark-hover" tabindex="-1">Example component</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>As an example, we use the following component that has a simple input and output property.
The component shows the input value using a <code>computed</code> and emits an event when a button is clicked.</p><pre id="d354a1b213a6739a26f6c974fe080f65"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="d354a1b213a6739a26f6c974fe080f65" data-status="d354a1b213a6739a26f6c974fe080f65-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="d354a1b213a6739a26f6c974fe080f65-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">@</span><span style="color: hsl(var(--syntax-constant-numeric))">Component</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  template</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">`</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">    &lt;div&gt;{{ greeting() }}&lt;/div&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">    &lt;button (click)=&quot;submitClick()&quot;&gt;Submit&lt;/button&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-property-value-css))">  `</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">export</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">GreetingsComponent</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">name</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">input</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">greet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">output</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">protected</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">greeting</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">computed</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">`Hello </span><span style="color: hsl(var(--syntax-invalid-deprecated))">${</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">name</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">!`</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">protected</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">submitClick</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">greet</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">emit</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">greeting</span><span style="color: hsl(var(--syntax-meta-tag))">())</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre>
		<h2 id="bindings">
		  <a href="#bindings" class="anchor mark-hover" tabindex="-1">Bindings</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>To set the component&#39;s properties, the <code>render</code> method options now support a <code>bindings</code> property, which is an array of the different binding methods.
This is also the case when using the <code>TestBed</code> to create the component, as it also supports a <code>bindings</code> property in the <code>createComponent</code> method options.</p>
		<h3 id="input-binding">
		  <a href="#input-binding" class="anchor mark-hover" tabindex="-1">Input binding</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>To set the input property, you can use the <code>inputBinding</code> method, which can be defined in the <code>bindings</code> property of the <code>render</code> options.
The example below sets the <code>name</code> input property using a signal and asserts that the greeting message is displayed.</p><pre id="9ed0211476a7d9d2afb16b41957806df"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="9ed0211476a7d9d2afb16b41957806df" data-status="9ed0211476a7d9d2afb16b41957806df-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="9ed0211476a7d9d2afb16b41957806df-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">inputBinding</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">signal</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/core'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">render</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@testing-library/angular'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">test</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'displays the greeting message'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">nameSignal</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">signal</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Alice'</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">render</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">GreetingsComponent</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    bindings</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span><span style="color: hsl(var(--syntax-constant-numeric))">inputBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'name'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">nameSignal</span><span style="color: hsl(var(--syntax-meta-tag))">)]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">expect</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">getByText</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Hello Alice!'</span><span style="color: hsl(var(--syntax-meta-tag))">))</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">toBeInTheDocument</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>As you can see, the <code>inputBinding</code> method takes two arguments: the name of the input property and a signal that holds the value of the input property.
Instead of using a signal, you can also use an inline method that returns the value.</p><pre id="e7dc964d3e2c878861d4985f6885125f"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="e7dc964d3e2c878861d4985f6885125f" data-status="e7dc964d3e2c878861d4985f6885125f-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="e7dc964d3e2c878861d4985f6885125f-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">inputBinding</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/core'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">render</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@testing-library/angular'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">test</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'displays the greeting message'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">render</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">GreetingsComponent</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    bindings</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span><span style="color: hsl(var(--syntax-constant-numeric))">inputBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'name'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Alice'</span><span style="color: hsl(var(--syntax-meta-tag))">)])]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">expect</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">getByText</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Hello Alice!'</span><span style="color: hsl(var(--syntax-meta-tag))">))</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">toBeInTheDocument</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>Input properties that are defined using an <code>alias</code> can also be set using the <code>inputBinding</code> method.
In this case, the alias name needs to be used instead of the property name.</p><pre id="230ada3c464865d61b14b9baea3fafe0"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="230ada3c464865d61b14b9baea3fafe0" data-status="230ada3c464865d61b14b9baea3fafe0-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="230ada3c464865d61b14b9baea3fafe0-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> An input with an alias</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">name</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">input</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">required</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> alias</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'personName'</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> The input binding uses the alias name to set the value</span></div><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">inputBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'personName'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Alice'</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre><p>In the past, we used <code>aliasedInput</code> in Angular Testing Library to set input properties with an alias, this will also become deprecated in future releases.</p>
		<h3 id="output-binding">
		  <a href="#output-binding" class="anchor mark-hover" tabindex="-1">Output binding</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>To test the output property of a component, you can now use the <code>outputBinding</code> method, which can also be defined in the <code>bindings</code> property of the <code>render</code> options.
Just as with the <code>inputBinding</code> method, the <code>outputBinding</code> method takes two arguments: the name of the output property and a callback function that will be called when the output event is emitted.</p><p>The second argument is great to pass a spy function, which is ideal to assert that the output event was emitted.
This was previously possible, but often required a type assertion to convince TypeScript that the callback function matches the output event type.</p><pre id="808e7662f2e937c0866d7ff97209ada0"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="808e7662f2e937c0866d7ff97209ada0" data-status="808e7662f2e937c0866d7ff97209ada0-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="808e7662f2e937c0866d7ff97209ada0-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">outputBinding</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/core'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">render</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@testing-library/angular'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-meta-tag))">userEvent</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@testing-library/user-event'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">test</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'emits the greeting message'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">user</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">userEvent</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">setup</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">greetSpy</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">vi</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">fn</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">render</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">GreetingsComponent</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    bindings</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span><span style="color: hsl(var(--syntax-constant-numeric))">inputBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'name'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Alice'</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">outputBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'greet'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">greetSpy</span><span style="color: hsl(var(--syntax-meta-tag))">)]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">user</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">click</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">getByRole</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'button'</span><span style="color: hsl(var(--syntax-meta-tag))">))</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">expect</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">greetSpy</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">toHaveBeenCalledWith</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Hello Alice!'</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre>
		<h3 id="two-way-binding">
		  <a href="#two-way-binding" class="anchor mark-hover" tabindex="-1">Two-way binding</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>For properties that require two-way binding (e.g. <a class="mark mark-hover" href="https://angular.dev/api/core/model" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fangular.dev)'><code>model</code></a>), you need to use the <code>twoWayBinding</code> method to set these properties.
If the greeting component would have a two-way binding for the <code>name</code> property, it could be set as follows.</p><p>In comparison to the <code>inputBinding</code> method, the <code>twoWayBinding</code> method only accepts a signal as the second argument, this is because two-way bindings require a writable value.</p><pre id="4bd2764d0f091b7b3505d109207d2f32"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="4bd2764d0f091b7b3505d109207d2f32" data-status="4bd2764d0f091b7b3505d109207d2f32-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="4bd2764d0f091b7b3505d109207d2f32-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">twoWayBinding</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">signal</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/core'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">render</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@testing-library/angular'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">test</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'displays the greeting message'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">nameSignal</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">signal</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Alice'</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">render</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">GreetingsComponent</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    bindings</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span><span style="color: hsl(var(--syntax-constant-numeric))">twoWayBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'name'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">nameSignal</span><span style="color: hsl(var(--syntax-meta-tag))">)]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  </span><span style="color: hsl(var(--syntax-constant-numeric))">expect</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">screen</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">getByText</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Hello Alice!'</span><span style="color: hsl(var(--syntax-meta-tag))">))</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">toBeInTheDocument</span><span style="color: hsl(var(--syntax-meta-tag))">()</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre>
		<h2 id="using-the-standard-angular-apis">
		  <a href="#using-the-standard-angular-apis" class="anchor mark-hover" tabindex="-1">Using the standard Angular APIs</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>As mentioned earlier, the <code>bindings</code> property is not specific to Angular Testing Library, but is part of the standard Angular testing APIs.
This means you&#39;re not required to use Angular Testing Library to use the new <code>bindings</code> functionality.</p><p>After configuring the <code>TestBed</code> (which is what Angular Testing Library does under the hood), you can create the component using the <code>createComponent</code> method and pass the <code>bindings</code> property in the options. Just as with the <code>render</code> method of Angular Testing Library, the <code>bindings</code> property is an array of the different binding methods.</p><pre id="49e98baa4432214f2c2cc216ccf7fb29"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="49e98baa4432214f2c2cc216ccf7fb29" data-status="49e98baa4432214f2c2cc216ccf7fb29-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="49e98baa4432214f2c2cc216ccf7fb29-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">TestBed</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/core/testing'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">import </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">inputBinding</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-meta-tag))">outputBinding</span><span style="color: hsl(var(--syntax-constant))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-constant))"> from </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'@angular/core'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">TestBed</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">configureTestingModule</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  declarations</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span><span style="color: hsl(var(--syntax-meta-tag))">DemoComponent</span><span style="color: hsl(var(--syntax-meta-tag))">]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">const</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">fixture</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">TestBed</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">createComponent</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">DemoComponent</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  bindings</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span><span style="color: hsl(var(--syntax-constant-numeric))">inputBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'name'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Alice'</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">outputBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'greet'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">greetSpy</span><span style="color: hsl(var(--syntax-meta-tag))">)]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre>
		<h2 id="some-questions-i-had-and-their-answers">
		  <a href="#some-questions-i-had-and-their-answers" class="anchor mark-hover" tabindex="-1">Some questions I had, and their answers</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2>
		<h3 id="can-i-use-this-api-with-input-and-output-decorators">
		  <a href="#can-i-use-this-api-with-input-and-output-decorators" class="anchor mark-hover" tabindex="-1">Can I use this API with @Input and @Output decorators?</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Yes, you can also make use the <code>inputBinding</code> and <code>outputBinding</code> methods with components that use the <code>@Input</code> and <code>@Output</code> decorators.</p>
		<h3 id="what-happens-if-i-dont-provide-a-required-input">
		  <a href="#what-happens-if-i-dont-provide-a-required-input" class="anchor mark-hover" tabindex="-1">What happens if I don&#39;t provide a required input?</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>If you don&#39;t provide a required input, Angular throws an error when the component is initialized.</p><pre id="4be352102e99808877107d7c806dd894"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="4be352102e99808877107d7c806dd894" data-status="4be352102e99808877107d7c806dd894-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="4be352102e99808877107d7c806dd894-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">NG0950:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">Input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">is</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">required</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">but</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">no</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">value</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">is</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">available</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">yet.</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">Find</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">more</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">at</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">https://angular.dev/errors/NG0950</span></div></code></pre>
		<h3 id="what-happens-if-i-provide-an-input-that-doesnt-exist">
		  <a href="#what-happens-if-i-provide-an-input-that-doesnt-exist" class="anchor mark-hover" tabindex="-1">What happens if I provide an input that doesn&#39;t exist?</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>One of the advantages of Angular Testing Library&#39;s API is that it provides type safety when setting input and output properties.
This is not (yet?) the case when using the <code>bindings</code> property, as it accepts strings for the property names.
However, Angular does perform runtime checks and will throw an error if you try to set a property that doesn&#39;t exist on the component.</p><pre id="b391047aa08a86f7680364efde88cda5"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="b391047aa08a86f7680364efde88cda5" data-status="b391047aa08a86f7680364efde88cda5-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="b391047aa08a86f7680364efde88cda5-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">NG0315:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">GreetingComponent</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">does</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">not</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">have</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">an</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">input</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">with</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">a</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">name</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">of</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;doesNotExist&quot;.</span></div></code></pre>
		<h3 id="can-i-update-signal-values-after-the-component-is-created">
		  <a href="#can-i-update-signal-values-after-the-component-is-created" class="anchor mark-hover" tabindex="-1">Can I update signal values after the component is created?</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Yes, you can update the signal values after the component is created.
To assert the template is updated, you need to call <code>fixture.detectChanges()</code> to trigger change detection.
Another option, which I prefer, is to use Angular Testing Library&#39;s retry utilities, such as <code>findByQuery</code>, which automatically retries until the element is found or a timeout occurs.</p>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The new <code>bindings</code> API in Angular Testing Library v18.1.0 and Angular v20.1 provides a standardized way to set input and output properties of components in tests.</p><p>The new API&#39;s offers a better experience with the Angular framework, making it easier to write and maintain tests for Angular components.
For example, it has a better integration with Angular&#39;s signals, and it reduces the need for type assertions when working with output events.
As a result, the existing methods in Angular Testing Library for setting input and output properties will be deprecated in future releases in favor of the new <code>bindings</code> property.</p><p>If you haven&#39;t tried Angular Testing Library yet, now is a great time to start using it in your Angular projects!</p><p>Putting it all together, here&#39;s a complete example using Angular Testing Library with the new <code>bindings</code> API:</p><pre id="f0b07a1618fd57cef5345f4eec7b3986"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.77842 8.51894L3 12.2974L6.77842 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.2216 8.51894L21 12.2974L17.2216 16.0758" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.27113 18.5948L14.7289 6.00001" stroke="#8B5CF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="f0b07a1618fd57cef5345f4eec7b3986" data-status="f0b07a1618fd57cef5345f4eec7b3986-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="f0b07a1618fd57cef5345f4eec7b3986-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="typescript " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">render</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-tag))">MyComponent</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  bindings</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> [</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">inputBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'value'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">signal</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'Angular Testing Library'</span><span style="color: hsl(var(--syntax-meta-tag))">))</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> supports signals</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">inputBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'greeting'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">'hello'</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> supports aliases</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">outputBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'clicked'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">clickHandlerSpy</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> easy to set spy functions</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant-numeric))">twoWayBinding</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'name'</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">signal</span><span style="color: hsl(var(--syntax-meta-tag))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">'initial'</span><span style="color: hsl(var(--syntax-meta-tag))">))</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">//</span><span style="color: hsl(var(--syntax-comment))"> two-way binding with signals</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">  ]</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))">)</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div></code></pre>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Forwarding authenticated calls to a downstream API using YARP]]></title>
					<description><![CDATA[A Backend for Frontend (BFF) typically communicates with downstream APIs on behalf of the user. In this post, we'll explore how to forward authenticated calls to a downstream API using YARP.]]></description>
					<link>https://timdeschryver.dev/blog/forwarding-authenticated-calls-to-a-downstream-api-using-yarp</link>
					<guid>https://timdeschryver.dev/blog/forwarding-authenticated-calls-to-a-downstream-api-using-yarp</guid>
					<pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/forwarding-authenticated-calls-to-a-downstream-api-using-yarp/images/banner.webp" alt="Forwarding authenticated calls to a downstream API using YARP"/><p>Read <strong>Forwarding authenticated calls to a downstream API using YARP</strong> on <a href="https://timdeschryver.dev/blog/forwarding-authenticated-calls-to-a-downstream-api-using-yarp">timdeschryver.dev</a></p><p>So far, we&#39;ve set up a Backend for Frontend (BFF) that handles the user authentication flow and serves as an intermediary between the frontend and backend services.
This BFF is the only application that&#39;s exposed to the public internet, while the other services are protected behind it.</p><p>While the BFF handles the first layer of security, it&#39;s crucial to ensure that the backend services also validate the user&#39;s identity and permissions. In this post, we&#39;ll explore how to forward authenticated calls to a downstream API using YARP (<a class="mark mark-hover" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/yarp/getting-started?WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'>Yet Another Reverse Proxy</a>) (the red arrow in the picture below). This is particularly useful in scenarios where you have a BFF architecture and need to securely communicate with other services on behalf of the user.</p><figure>
				<img src="https://timdeschryver.dev/blog/forwarding-authenticated-calls-to-a-downstream-api-using-yarp/images/architecture.webp" alt="" loading="lazy"/>
				<figcaption>The overall architecture of the application</figcaption>
			</figure><div class="custom-block info">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z"/><path d="M15 3v4a2 2 0 0 0 2 2h4"/></svg></span>Note</div>
			<p>As we&#39;ve discussed in previous posts, the BFF pattern helps to centralize the handling of authentication and authorization, making it easier to manage security concerns. By using YARP, we can efficiently route requests from our BFF to downstream APIs while ensuring that the user&#39;s identity and permissions are correctly propagated.</p>
		</div>
		<h2 id="how-does-service-to-service-authentication-work">
		  <a href="#how-does-service-to-service-authentication-work" class="anchor mark-hover" tabindex="-1">How does service-to-service authentication work</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The BFF stores a cookie with user&#39;s session information, and this information needs to be passed down to the next API in the chain. However, we&#39;re not going to forward the cookie directly. Instead of forwarding the cookie, we will obtain the access token from the user&#39;s session using <a class="mark mark-hover" href="https://datatracker.ietf.org/doc/html/rfc8693" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fdatatracker.ietf.org)'>OAuth 2.0 Token Exchange</a>, and include the token in the Authorization header of the request towards the downstream API. This practice makes the setup scalable, and we&#39;re following the best practices for building API backends.</p>
		<h2 id="why-is-an-access-token-preferred">
		  <a href="#why-is-an-access-token-preferred" class="anchor mark-hover" tabindex="-1">Why is an access token preferred</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>But why do we add this overhead instead of forwarding the cookie directly? There are several reasons:</p><ul>
<li>The API remains stateless: By using access tokens, we can ensure that the downstream API remains stateless. This means that the API doesn&#39;t need to maintain any session state, which simplifies scaling and reduces complexity.</li>
<li>A token is more portable: Access tokens can be easily passed between different services and applications, while cookies are tied to a specific domain and may not be accessible by other services.</li>
<li>The size of the token is smaller: Access tokens are typically smaller in size compared to cookies. While this is negligible for a single request, it adds up when dealing with a high volume of requests. This can lead to reduced network overhead and improved performance.</li>
</ul>

		<h2 id="implementing-the-solution">
		  <a href="#implementing-the-solution" class="anchor mark-hover" tabindex="-1">Implementing the solution</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2>
		<h3 id="setup-using-duendeaccesstokenmanagementopenidconnect">
		  <a href="#setup-using-duendeaccesstokenmanagementopenidconnect" class="anchor mark-hover" tabindex="-1">Setup using Duende.AccessTokenManagement.OpenIdConnect</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>While it&#39;s possible to retrieve the access token manually (through <code>context.HttpContext.GetTokenAsync(&quot;access_token&quot;);</code>), it doesn&#39;t automatically handle token expiration and most importantly the refresh cycle when the token is expired.
To simplify the implementation, we can use the <a class="mark mark-hover" href="https://docs.duendesoftware.com/accesstokenmanagement/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fdocs.duendesoftware.com)'>Duende.AccessTokenManagement.OpenIdConnect</a> library, which provides built-in support for token management, including automatic token refresh. This functionality was also <a class="mark mark-hover" href="https://github.com/dotnet/aspnetcore/issues/8175" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>planned</a> to be included in ASP.NET v10, but sadly it&#39;s postponed for now.</p><p>To install the Duende library, run the following command in your BFF project:</p><pre id="ccb0acbd6810d0da0c4d3bb6c6a8b2a6"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="ccb0acbd6810d0da0c4d3bb6c6a8b2a6" data-status="ccb0acbd6810d0da0c4d3bb6c6a8b2a6-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="ccb0acbd6810d0da0c4d3bb6c6a8b2a6-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">Duende.AccessTokenManagement.OpenIdConnect</span></div></code></pre><p>Next, register the access token management services using <code>AddOpenIdConnectAccessTokenManagement()</code> in your <code>Program.cs</code> file.
Because it makes use of a cache to store the tokens, you might also need to add a cache implemention, the example below uses the distributed memory cache.</p><pre id="220cdd74133beb25de2c17ba7e22f00d"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Program.cs</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Gateway/Program.cs#L13-L14" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="220cdd74133beb25de2c17ba7e22f00d" data-status="220cdd74133beb25de2c17ba7e22f00d-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="220cdd74133beb25de2c17ba7e22f00d-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line highlight"><span style="color: hsl(var(--syntax-constant))">using</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Duende</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">AccessTokenManagement</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">OpenIdConnect</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddServiceDefaults</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddReverseProxy</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Configuration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddAuthenticationSchemes</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Configuration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddDistributedMemoryCache</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddOpenIdConnectAccessTokenManagement</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProblemDetails</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">UseStatusCodePages</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">UseExceptionHandler</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">UseAuthentication</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">UseAuthorization</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapGroup</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;bff&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapUserEndpoints</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapReverseProxy</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapDefaultEndpoints</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre>
		<h3 id="create-a-yarp-transformer">
		  <a href="#create-a-yarp-transformer" class="anchor mark-hover" tabindex="-1">Create a YARP transformer</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>YARP provides a flexible way to modify requests and responses using <a class="mark mark-hover" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/yarp/transforms?view=aspnetcore-9.0&WT.mc_id=DT-MVP-5004452" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Flearn.microsoft.com)'>transformers</a>.</p><p>To modify the outgoing requests and add the access token to the Authorization header, we need to create a custom transformer by implementing the <code>RequestTransform</code> base class.</p><p>The transformer below checks if the user is authenticated, retrieves the access token using the extension method <code>GetUserAccessTokenAsync()</code> (coming from the Duende library), and adds it to the Authorization header of the outgoing request.</p><pre id="c97e46b8307c314040be930f314ca23c"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">AddBearerTokenToHeadersTransform.cs</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Gateway/Transformers/AddBearerTokenToHeadersRequestTransform.cs" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="c97e46b8307c314040be930f314ca23c" data-status="c97e46b8307c314040be930f314ca23c-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="c97e46b8307c314040be930f314ca23c-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">internal</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">sealed</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">class</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">AddBearerTokenToHeadersTransform</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-name-section))">ILogger</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">AddBearerTokenToHeadersTransform</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">logger</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">RequestTransform</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">override</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">async</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">ValueTask</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">ApplyAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-name-section))">RequestTransformContext</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">context</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">HttpContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">User</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Identity</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">is</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">not</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">IsAuthenticated</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">        //</span><span style="color: hsl(var(--syntax-comment))"> This also handles token refreshes</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">accessToken</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">await</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">HttpContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetUserAccessTokenAsync</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-meta-tag))">accessToken</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Succeeded</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">logger</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">LogError</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Could not get access token: {GetUserAccessTokenError} for request path: {RequestPath}. {Error}&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">accessToken</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">FailedResult</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Error</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">HttpContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Request</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">accessToken</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">FailedResult</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ErrorDescription</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">logger</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">LogInformation</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Adding bearer token to request headers for request path: {RequestPath}&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">HttpContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Request</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Path</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ProxyRequest</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Headers</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Authorization</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">AuthenticationHeaderValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Bearer&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">accessToken</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Token</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AccessToken</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>The method <code>GetUserAccessTokenAsync()</code> takes care of retrieving the access token from the user&#39;s session and handles token refreshes if necessary.</p><div class="custom-block tip">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 14c.2-1 .7-1.7 1.5-2.5A4.8 4.8 0 0 0 18 8 6 6 0 0 0 6 8c0 1.3.5 2.5 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg></span>Tip</div>
			<p>If you&#39;re not using YARP, you can still use the same logic to add the access token to the outgoing HTTP requests of a <code>HttpClient</code>.
For more information, check the <a class="mark mark-hover" href="https://docs.duendesoftware.com/accesstokenmanagement/web-apps/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fdocs.duendesoftware.com)'>official documentation</a>.</p>
		</div>
		<h3 id="register-the-transformer-with-yarp">
		  <a href="#register-the-transformer-with-yarp" class="anchor mark-hover" tabindex="-1">Register the transformer with YARP</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>Finally, we need to register the custom transformer with YARP in the <code>AddReverseProxy</code> method. This can be done by adding a call to <code>AddTransforms</code> and including our transformer in the configuration. Because we only want to add the access token when the route requires authentication, we can check if the <code>AuthorizationPolicy</code> is set on the route. This is a step not to be missed, as we don&#39;t want to add the Authorization header when it&#39;s not needed, for example when calling external services that are not in our control.</p><pre id="2b8086a7d432bfb58e456d2afa470be8"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Extensions.cs</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Gateway/Extensions.cs#L24-L34" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="2b8086a7d432bfb58e456d2afa470be8" data-status="2b8086a7d432bfb58e456d2afa470be8-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="2b8086a7d432bfb58e456d2afa470be8-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">static</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IServiceCollection</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">AddReverseProxy</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IServiceCollection</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">services</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IConfiguration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">configuration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddSingleton</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">AddBearerTokenToHeadersTransform</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">services</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddReverseProxy</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">LoadFromConfig</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetSection</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;ReverseProxy&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">))</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddTransforms</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">builderContext</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IsNullOrEmpty</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">builderContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Route</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthorizationPolicy</span><span style="color: hsl(var(--syntax-invalid-deprecated))">))</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">builderContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">RequestTransforms</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Add</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">builderContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetRequiredService</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">AddBearerTokenToHeadersTransform</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;());</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line highlight empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">builderContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">RequestTransforms</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Add</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">RequestHeaderRemoveTransform</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Cookie&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">));</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddServiceDiscoveryDestinationResolver</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">services</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre>
		<h3 id="remove-the-cookie-from-the-outgoing-request">
		  <a href="#remove-the-cookie-from-the-outgoing-request" class="anchor mark-hover" tabindex="-1">Remove the cookie from the outgoing request</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>After all previous steps, the outgoing request will contain both the Authorization header with the access token.
This means we can remove the cookie from the outgoing request, which is done by adding a <code>RequestHeaderRemoveTransform</code> to the transforms collection.</p><pre id="2b8086a7d432bfb58e456d2afa470be8"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Extensions.cs</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Gateway/Extensions.cs#L24-L34" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="2b8086a7d432bfb58e456d2afa470be8" data-status="2b8086a7d432bfb58e456d2afa470be8-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="2b8086a7d432bfb58e456d2afa470be8-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">static</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IServiceCollection</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">AddReverseProxy</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IServiceCollection</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">services</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IConfiguration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">configuration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddSingleton</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">AddBearerTokenToHeadersTransform</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">services</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddReverseProxy</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">LoadFromConfig</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetSection</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;ReverseProxy&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">))</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddTransforms</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">builderContext</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">if</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">!</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">IsNullOrEmpty</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">builderContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Route</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthorizationPolicy</span><span style="color: hsl(var(--syntax-invalid-deprecated))">))</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">builderContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">RequestTransforms</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Add</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">builderContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetRequiredService</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-entity-name-section))">AddBearerTokenToHeadersTransform</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;());</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">builderContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">RequestTransforms</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Add</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">RequestHeaderRemoveTransform</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;Cookie&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">));</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddServiceDiscoveryDestinationResolver</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">services</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre>
		<h2 id="conclusion">
		  <a href="#conclusion" class="anchor mark-hover" tabindex="-1">Conclusion</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>In this post, we&#39;ve explored how to forward authenticated calls to a downstream API using YARP in a BFF architecture. By leveraging the <code>Duende.AccessTokenManagement.OpenIdConnect</code> library, we simplified the process of managing access tokens and ensured that our backend services can securely authorize requests on behalf of the user.
By simply configuring YARP to use a custom transformer, we can keep this logic encapsulated and reusable across different routes and services.
This approach enhances the security of our application architecture.</p><p>For the full source code, check out the <a class="mark mark-hover" href="https://github.com/timdeschryver/Sandbox" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>Sandbox repository</a>.</p>]]></content:encoded>
				</item>
			

				<item>
					<title><![CDATA[Secure your Yarp BFF with cookie-based authentication]]></title>
					<description><![CDATA[Learn how to implement a secure cookie-based authentication in a YARP (Yet Another Reverse Proxy) Project that acts as the Backend for Frontend (BFF) architecture within a .NET Aspire solution. We also cover why we should prefer cookies over tokens for authentication (spoiler, it's security related).]]></description>
					<link>https://timdeschryver.dev/blog/secure-your-yarp-bff-with-cookie-based-authentication</link>
					<guid>https://timdeschryver.dev/blog/secure-your-yarp-bff-with-cookie-based-authentication</guid>
					<pubDate>Thu, 11 Sep 2025 00:00:00 GMT</pubDate>
					<dc:creator><![CDATA[Tim Deschryver]]></dc:creator>
					<content:encoded><![CDATA[<img class="webfeedsFeaturedVisual" src="https://timdeschryver.dev/blog/secure-your-yarp-bff-with-cookie-based-authentication/images/banner.webp" alt="Secure your Yarp BFF with cookie-based authentication"/><p>Read <strong>Secure your Yarp BFF with cookie-based authentication</strong> on <a href="https://timdeschryver.dev/blog/secure-your-yarp-bff-with-cookie-based-authentication">timdeschryver.dev</a></p><p>The first step towards building a secure system is a good authentication mechanism.
This is your first layer of defense from an application security standpoint.
One of the reasons the BFF (Backend for Frontend) pattern is used, is exactly to provide a solid foundation.</p><p>In this post I will cover the following items:</p><ul>
<li>Adding cookie-based authentication to a ASP.NET (API) Application</li>
<li>Authenticating a user through an identity provider (Auth0 in my case)</li>
<li>Creating endpoints to login, logout and get user information (this will later-on be consumed by the application)</li>
</ul>
<p>I&#39;m using my <a class="mark mark-hover" href="https://github.com/timdeschryver/Sandbox" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>Sandbox</a> as demo application, which follows the following architecture within a .NET Aspire solution:</p><figure>
				<img src="https://timdeschryver.dev/blog/secure-your-yarp-bff-with-cookie-based-authentication/images/architecture.webp" alt="" loading="lazy"/>
				<figcaption>High-level architecture of the Sandbox application.</figcaption>
			</figure><p>The application is publicly available via the Proxy/Gateway (YARP) project, which redirects the requests to the correct internal service. Since the user interacts with the application through the Proxy, it&#39;s also the YARP application that handles authentication flow and knows about the user session.</p><p>The focus for this post is the red circled area of the architectural diagram. In future posts, I will cover how to secure the internal systems (.NET and Angular) as well.</p>
		<h2 id="token-based-authentication">
		  <a href="#token-based-authentication" class="anchor mark-hover" tabindex="-1">Token-based authentication</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>Many applications (still) make use of JWT tokens to store the information of an authenticated user.
The token is stored in the client state (e.g. in the browser&#39;s local or session storage), and is added to each request (via the <code>Authorization</code> header) to the backend API.</p><p>Because this all happens within the client, it&#39;s vulnerable to attacks. An attacker can get ahold of your token, and once an attacker gets access to your token, it can impersonate you, meaning it can send requests on behalf of you. Therefore, to limit the attack window, it&#39;s encouraged to create tokens with a short lifetime. While the token is encoded, it&#39;s easy to decode the token to read the contents. For example see <a class="mark mark-hover" href="https://jwt.ms/" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fjwt.ms)'>jwt.ms</a>, here you can paste your token to see the contents.</p><p>Often the token contains more information than needed, including sensitive information, which increases the risk of leaking sensitive information.
Therefore, it&#39;s best to keep the token as small as possible, and only include the necessary information (e.g. user id, roles, permissions).</p><p>A more secure way to authenticate a user, is to use a cookie.</p>
		<h2 id="cookie-based-authentication">
		  <a href="#cookie-based-authentication" class="anchor mark-hover" tabindex="-1">Cookie-based authentication</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>The main difference between token-based and cookie-based authentication, is the location where the authentication information is stored. With cookie-based authentication, the session identifier (cookie) is stored on the server. This drastically reduces the attack surface, as there&#39;s no sensitive information stored client-side.</p><div class="custom-block tip">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 14c.2-1 .7-1.7 1.5-2.5A4.8 4.8 0 0 0 18 8 6 6 0 0 0 6 8c0 1.3.5 2.5 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg></span>Tip</div>
			<p>While taking this approach is also important to configure the cookie as:</p><ul>
<li><code>HTTP-only</code>, so it can&#39;t be accessed via JavaScript, which protects against XSS attacks</li>
<li><code>SameSite=Strict|Lax</code>, to prevent CSRF attacks</li>
<li><code>SecurePolicy=Always</code>, to ensure the cookie is only sent over HTTPS connections</li>
</ul>

		</div><p>But that&#39;s not all, cookies have multiple security features built-in, which makes them a more secure option compared to tokens.
Using a cookie also allows the server to manage the session, and invalidate it when needed (e.g. logout, session expiration, etc.). This is not possible with token-based authentication, as the token is stateless and can&#39;t (easily) be invalidated server-side.</p><p>From a user experience standpoint, cookie-based authentication also allows for a smoother experience, as it allows you to implement a Single Sign-On (SSO) experience, in order to avoid multiple login prompts when accessing different applications within the same domain.</p><p>The way cookie-based authentication works, is that when a user successfully authenticates, the server creates stores (in-memory, a database, Redis, …) the session information. When the user makes a request to the server, the cookie is automatically attached to the request by the browser. The server then validates the cookie and retrieves the session information to authenticate the user.</p><p>The authentication complexity moves from the client to the server, and is often also less complex to implement the whole authentication flow.</p><div class="custom-block info">
			<div class="custom-block-title"><span class="custom-block-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z"/><path d="M15 3v4a2 2 0 0 0 2 2h4"/></svg></span>Note</div>
			<p>The cookie based authentication only is applicable for web applications within a browser context, as cookies are a browser feature. For mobile or desktop applications, token-based authentication (using the PKCE flow) is still the preferred way to go.</p>
		</div>
		<h2 id="implementing-cookie-based-authentication-within-a-yarp-bff">
		  <a href="#implementing-cookie-based-authentication-within-a-yarp-bff" class="anchor mark-hover" tabindex="-1">Implementing cookie-based authentication within a YARP BFF</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>In my previous post <a class="mark mark-hover" href="https://timdeschryver.dev/blog/integrating-yarp-within-dotnet-aspire" data-sveltekit-reload >Using YARP as BFF within .NET Aspire: Integrating YARP into .NET Aspire</a>, we set up a YARP gateway as a BFF. We will continue building on top of that project, and add authentication to it.</p><p>To implement cookie-based authentication, no additional NuGet packages are needed, as ASP.NET Core has built-in support for cookie authentication. But, because we want to authenticate the user through an identity provider (Auth0 in my case), we need to add the <code>Microsoft.AspNetCore.Authentication.OpenIdConnect</code> package. This package provides handles the communication with the identity provider using the OpenID Connect (OIDC) protocol.</p><pre id="da7283fb829cee28c1e9e322b13646a1"><div class="code-heading"><svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33643 17L10.3364 11L4.33643 5" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3364 19H20.3364" stroke="#14B8A6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
 <button type="button" class="copy-code icon align-text-top" data-ref="da7283fb829cee28c1e9e322b13646a1" data-status="da7283fb829cee28c1e9e322b13646a1-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="da7283fb829cee28c1e9e322b13646a1-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="bash " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant-numeric))">dotnet</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">add</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">package</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">Microsoft.AspNetCore.Authentication.OpenIdConnect</span></div></code></pre><p>Next, we can configure the authentication schemes for the application.</p><p>Using the <code>AddAuthentication</code> method, the authentication middleware is registered.
The primary scheme is set to the cookie authentication scheme, and the challenge scheme to the OpenID Connect scheme.
The challenge scheme will be used when the user is not authenticated and tries to access a protected resource. When this happens, the challenge scheme is invoked. In this case it redirects the user to the identity provider for authentication.</p><p>The next methods, <code>AddCookie</code> and <code>AddOpenIdConnect</code>, configures the respective authentication schemes.
The configuration for the cookie scheme is straightforward, we set the cookie name, and configure the security settings.
The OpenID Connect configuration is a bit more complex, as it requires multiple settings to be configured - you can find these within the settings of your identity provider -.</p><pre id="c47617ed4b891386c54740548aa76661"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Extensions.cs</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Gateway/Extensions.cs" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="c47617ed4b891386c54740548aa76661" data-status="c47617ed4b891386c54740548aa76661-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="c47617ed4b891386c54740548aa76661-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">static</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IServiceCollection</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">AddAuthenticationSchemes</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IServiceCollection</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">services</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IConfiguration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">configuration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddAuthentication</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">DefaultScheme</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">CookieAuthenticationDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">DefaultChallengeScheme</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">OpenIdConnectDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddCookie</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">CookieAuthenticationDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Cookie</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Name</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;__Sandbox&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Cookie</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">SameSite</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Microsoft</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AspNetCore</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Http</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">SameSiteMode</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Strict</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Cookie</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">SecurePolicy</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">CookieSecurePolicy</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Always</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddOpenIdConnect</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">OpenIdConnectDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Authority</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">$&quot;https://</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:Domain&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ClientId</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:ClientId&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ClientSecret</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:ClientSecret&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ResponseType</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">OpenIdConnectResponseType</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Code</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ResponseMode</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">OpenIdConnectResponseMode</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Query</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">GetClaimsFromUserInfoEndpoint</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">SaveTokens</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">MapInboundClaims</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">TokenValidationParameters</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">TokenValidationParameters</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">NameClaimType</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">ClaimTypes</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">NameIdentifier</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">RoleClaimType</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">ClaimTypes</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Role</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Scope</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Clear</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Scope</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Add</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;openid&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Scope</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Add</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;profile&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Scope</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Add</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;offline_access&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Events</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">OnRedirectToIdentityProviderForSignOut</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">context</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">logoutUri</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">$&quot;https://</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:Domain&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">/oidc/logout?client_id=</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:ClientId&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">redirectUrl</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">HttpContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">BuildRedirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Properties</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">RedirectUri</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">logoutUri</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">+=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">$&quot;&amp;post_logout_redirect_uri=</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">redirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Response</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Redirect</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">logoutUri</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">HandleResponse</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Task</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">CompletedTask</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">OnRedirectToIdentityProvider</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">context</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">                //</span><span style="color: hsl(var(--syntax-comment))"> Auth0 specific parameter to specify the audience</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ProtocolMessage</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">SetParameter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;audience&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:Audience&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">));</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Task</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">CompletedTask</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">services</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>That&#39;s it for the authentication part, but we also want to enforce authorization.
Register the authorization middleware in the request pipeline by calling <code>AddAuthorization</code>.
Within the callback, set the default authorization policy to the cookie authentication scheme, and require an authenticated user.
This ensures that all authorized endpoints are protected by default (if no policies are specified), and only authenticated users can access them.</p><pre id="d5924af6ec9ace5550d3815674faa197"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Extensions.cs</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Gateway/Extensions.cs" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="d5924af6ec9ace5550d3815674faa197" data-status="d5924af6ec9ace5550d3815674faa197-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="d5924af6ec9ace5550d3815674faa197-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">public</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">static</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IServiceCollection</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">AddAuthenticationSchemes</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IServiceCollection</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">services</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IConfiguration</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">configuration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddAuthentication</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">DefaultScheme</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">CookieAuthenticationDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">DefaultChallengeScheme</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">OpenIdConnectDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddCookie</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">CookieAuthenticationDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Cookie</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Name</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;__Sandbox&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Cookie</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">SameSite</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Microsoft</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AspNetCore</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Http</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">SameSiteMode</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Strict</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Cookie</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">SecurePolicy</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">CookieSecurePolicy</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Always</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">})</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddOpenIdConnect</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">OpenIdConnectDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Authority</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">$&quot;https://</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:Domain&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ClientId</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:ClientId&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ClientSecret</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:ClientSecret&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ResponseType</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">OpenIdConnectResponseType</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Code</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ResponseMode</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">OpenIdConnectResponseMode</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Query</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">GetClaimsFromUserInfoEndpoint</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">SaveTokens</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">MapInboundClaims</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">TokenValidationParameters</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">TokenValidationParameters</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">NameClaimType</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">ClaimTypes</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">NameIdentifier</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">RoleClaimType</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">ClaimTypes</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Role</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Scope</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Clear</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Scope</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Add</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;openid&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Scope</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Add</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;profile&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Scope</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Add</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;offline_access&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Events</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">OnRedirectToIdentityProviderForSignOut</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">context</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">logoutUri</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">$&quot;https://</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:Domain&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">/oidc/logout?client_id=</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:ClientId&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">redirectUrl</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">HttpContext</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">BuildRedirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Properties</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">RedirectUri</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">logoutUri</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">+=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">$&quot;&amp;post_logout_redirect_uri=</span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))">redirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Response</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Redirect</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">logoutUri</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">HandleResponse</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Task</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">CompletedTask</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">OnRedirectToIdentityProvider</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">context</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">                //</span><span style="color: hsl(var(--syntax-comment))"> Auth0 specific parameter to specify the audience</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">ProtocolMessage</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">SetParameter</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;audience&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">configuration</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">GetValue</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&lt;</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">&gt;(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;OpenIDConnectSettings:Audience&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">));</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">                </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Task</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">CompletedTask</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddAuthorization</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">options</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">options</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">DefaultPolicy</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">AuthorizationPolicyBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">CookieAuthenticationDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">RequireAuthenticatedUser</span><span style="color: hsl(var(--syntax-invalid-deprecated))">()</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">services</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>Finally, enable the authentication and authorization middleware in the request pipeline, by calling <code>UseAuthentication</code> and <code>UseAuthorization</code>. Don&#39;t worry about the <code>MapUserEndpoints</code> method for now, this is covered in the next section.</p><pre id="f95000bae994d654eb241ae225c1b2f9"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">Program.cs</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Gateway/Prorgram.cs" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="f95000bae994d654eb241ae225c1b2f9" data-status="f95000bae994d654eb241ae225c1b2f9-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="f95000bae994d654eb241ae225c1b2f9-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs dim" tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">using</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Sandbox</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Gateway</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">using</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Sandbox</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">Gateway</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">UserModule</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">using</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">Sandbox</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-entity-name-section))">ServiceDefaults</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">WebApplication</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">CreateBuilder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">args</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddServiceDefaults</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddReverseProxy</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Configuration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddAuthenticationSchemes</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Configuration</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Services</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">AddProblemDetails</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">app</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Build</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">UseStatusCodePages</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">UseExceptionHandler</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">UseAuthentication</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">UseAuthorization</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapGroup</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;bff&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line highlight"><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapUserEndpoints</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapReverseProxy</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapDefaultEndpoints</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">app</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Run</span><span style="color: hsl(var(--syntax-invalid-deprecated))">();</span></div></code></pre>
		<h3 id="endpoints-for-authentication-flow">
		  <a href="#endpoints-for-authentication-flow" class="anchor mark-hover" tabindex="-1">Endpoints for authentication flow</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h3><p>To handle the authentication flow, we need to create a few endpoints to manage the user session, such as the login and logout behaviors. These will be called by the frontend application to initiate the authentication process.</p><p>The login endpoint returns a challenge result, which triggers the authentication middleware to redirect the user to the identity provider for authentication. To redirect the user back to a specific URL after a successful login, we provide a <code>returnUrl</code> parameter.</p><p>The same applies to the logout endpoint, which instead returns a sign-out result, which signs the user out from both the cookie and OIDC schemes. Again, we provide a <code>redirectUrl</code> parameter to redirect the user back to a specific URL after a successful logout.</p><pre id="0527712564c47feb00154ae0f16d9ba0"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">User.cs</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Gateway/UserModule/UserModule.cs" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="0527712564c47feb00154ae0f16d9ba0" data-status="0527712564c47feb00154ae0f16d9ba0-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="0527712564c47feb00154ae0f16d9ba0-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-constant))">internal</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">static</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IEndpointRouteBuilder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">MapUserEndpoints</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">this</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">IEndpointRouteBuilder</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">builder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapGet</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;login&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">returnUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">HttpContext</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">context</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">properties</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">AuthenticationProperties</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">RedirectUri</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">BuildRedirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">returnUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">TypedResults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Challenge</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapGet</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;logout&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant))">string</span><span style="color: hsl(var(--syntax-invalid-deprecated))">?</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">redirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">HttpContext</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">context</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">properties</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">AuthenticationProperties</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">RedirectUri</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">context</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">BuildRedirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">redirectUrl</span><span style="color: hsl(var(--syntax-invalid-deprecated))">),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">TypedResults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">SignOut</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">properties</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">[</span><span style="color: hsl(var(--syntax-meta-tag))">CookieAuthenticationDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">OpenIdConnectDefaults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">AuthenticationScheme</span><span style="color: hsl(var(--syntax-invalid-deprecated))">]);</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-invalid-deprecated))">;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div></code></pre><p>The third useful endpoint is the user endpoint that returns the current user information and claims.
To retrieve the user information, we can use the <code>ClaimsPrincipal</code> provided by ASP.NET Core, which represents the current authenticated user (from the cookie). You can extend this to return more information for your use case, for example application-specific data.</p><pre id="88116e46aac51b95abc668da7b39a238"><div class="code-heading"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.2628 9.9375H12.7741C12.7287 9.61553 12.6359 9.32955 12.4957 9.07955C12.3556 8.82576 12.1757 8.60985 11.956 8.43182C11.7363 8.25379 11.4825 8.11742 11.1946 8.02273C10.9105 7.92803 10.6018 7.88068 10.2685 7.88068C9.66619 7.88068 9.14157 8.0303 8.6946 8.32955C8.24763 8.625 7.90104 9.05682 7.65483 9.625C7.40862 10.1894 7.28551 10.875 7.28551 11.6818C7.28551 12.5114 7.40862 13.2083 7.65483 13.7727C7.90483 14.3371 8.25331 14.7633 8.70028 15.0511C9.14725 15.339 9.6643 15.483 10.2514 15.483C10.581 15.483 10.8859 15.4394 11.1662 15.3523C11.4503 15.2652 11.7022 15.1383 11.9219 14.9716C12.1416 14.8011 12.3234 14.5947 12.4673 14.3523C12.6151 14.1098 12.7173 13.8333 12.7741 13.5227L15.2628 13.5341C15.1984 14.0682 15.0374 14.5833 14.7798 15.0795C14.526 15.572 14.1832 16.0133 13.7514 16.4034C13.3234 16.7898 12.812 17.0966 12.2173 17.3239C11.6264 17.5473 10.9579 17.6591 10.2116 17.6591C9.17377 17.6591 8.24574 17.4242 7.42756 16.9545C6.61316 16.4848 5.96922 15.8049 5.49574 14.9148C5.02604 14.0246 4.79119 12.947 4.79119 11.6818C4.79119 10.4129 5.02983 9.33333 5.5071 8.44318C5.98438 7.55303 6.6321 6.875 7.45028 6.40909C8.26847 5.93939 9.18892 5.70455 10.2116 5.70455C10.8859 5.70455 11.5109 5.79924 12.0866 5.98864C12.6662 6.17803 13.1795 6.45455 13.6264 6.81818C14.0734 7.17803 14.437 7.61932 14.7173 8.14205C15.0014 8.66477 15.1832 9.26326 15.2628 9.9375Z" fill="#14B8A6"/>
<path d="M20.2726 16.4693L21.7391 7.53064H23.1358L21.6693 16.4693H20.2726ZM16.2703 14.2521L16.5016 12.8555H23.485L23.2537 14.2521H16.2703ZM17.1301 16.4693L18.5966 7.53064H19.9933L18.5268 16.4693H17.1301ZM16.781 11.1445L17.0166 9.74786H24L23.7643 11.1445H16.781Z" fill="#14B8A6"/>
</svg>
 <span class="file-name">User.cs</span> <a href="https://github.com/timdeschryver/Sandbox/blob/main/Sandbox.Gateway/UserModule/UserModule.cs" class="icon align-text-top" target="_blank" rel="noopener noreferrer" title="View source"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> <button type="button" class="copy-code icon align-text-top" data-ref="88116e46aac51b95abc668da7b39a238" data-status="88116e46aac51b95abc668da7b39a238-copy-status" aria-label="Copy code"><span class="copy-code-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg></span></button><span id="88116e46aac51b95abc668da7b39a238-copy-status" class="screen-reader-only" aria-live="polite"></span></div><code class="cs " tabindex="0"><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">builder</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">MapGet</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;user&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-entity-name-section))">ClaimsPrincipal</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">principal</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">var</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">user</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">principal</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">switch</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Identity</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">IsAuthenticated</span><span style="color: hsl(var(--syntax-invalid-deprecated))">:</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">User</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">IsAuthenticated</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">true</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">Name</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">principal</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Claims</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">SingleOrDefault</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">c</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">c</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Type</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">==</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-property-value-css))">&quot;name&quot;</span><span style="color: hsl(var(--syntax-invalid-deprecated))">)</span><span style="color: hsl(var(--syntax-constant))">?.</span><span style="color: hsl(var(--syntax-meta-tag))">Value</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">Claims</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">principal</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Claims</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Select</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-constant-numeric))">c</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">UserClaim</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Type</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">c</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Type</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">Value</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">c</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-meta-tag))">Value</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}),</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">},</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-meta-tag))">_</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=&gt;</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">new</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-entity-name-section))">User</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">{</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">IsAuthenticated</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">false</span><span style="color: hsl(var(--syntax-invalid-deprecated))">,</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">            </span><span style="color: hsl(var(--syntax-meta-tag))">Name</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant))">=</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-constant-numeric))">null</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">        </span><span style="color: hsl(var(--syntax-invalid-deprecated))">}</span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-invalid-deprecated))">};</span></div><div class="line empty"><span> </span></div><div class="line "><span style="color: hsl(var(--syntax-meta-tag))">    </span><span style="color: hsl(var(--syntax-constant))">return</span><span style="color: hsl(var(--syntax-meta-tag))"> </span><span style="color: hsl(var(--syntax-meta-tag))">TypedResults</span><span style="color: hsl(var(--syntax-constant))">.</span><span style="color: hsl(var(--syntax-constant-numeric))">Ok</span><span style="color: hsl(var(--syntax-invalid-deprecated))">(</span><span style="color: hsl(var(--syntax-meta-tag))">user</span><span style="color: hsl(var(--syntax-invalid-deprecated))">);</span></div><div class="line "><span style="color: hsl(var(--syntax-invalid-deprecated))">});</span></div></code></pre>
		<h2 id="summary">
		  <a href="#summary" class="anchor mark-hover" tabindex="-1">Summary</a>
		  <span class="icon align-text-top"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span>
		</h2><p>With the above code, we have successfully secured our YARP BFF application using cookie-based authentication.
We chose the cookie authentication scheme for its security benefits, as well as a less complex implementation compared to token-based authentication.
This sets a solid security foundation to build upon, as the BFF now handles the authentication flow and manages the user session securely.</p><p>As the result we have the following endpoints, which can be consumed (in the next post) by the frontend application:</p><ul>
<li><code>/bff/user</code>: Returns current user information and claims</li>
<li><code>/bff/login</code>: Initiates OIDC authentication flow</li>
<li><code>/bff/logout</code>: Signs out user from both cookie and OIDC schemes</li>
</ul>
<p>For the full source code, check out the <a class="mark mark-hover" href="https://github.com/timdeschryver/Sandbox" rel="external" data-with-favicon style='--favicon: url(https://v1.indieweb-avatar.11ty.dev/https%3A%2F%2Fgithub.com)'>Sandbox repository</a>.</p>]]></content:encoded>
				</item>
			
		</channel>
		</rss>