<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Sogl</title>
    <description>The latest articles on DEV Community by Daniel Sogl (@danielsogl).</description>
    <link>https://dev.to/danielsogl</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1395106%2F267c5ba1-5a1a-4633-8340-95835760da17.jpg</url>
      <title>DEV Community: Daniel Sogl</title>
      <link>https://dev.to/danielsogl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danielsogl"/>
    <language>en</language>
    <item>
      <title>The Skill AI Adoption Actually Requires: Leadership</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Sun, 08 Mar 2026 11:55:54 +0000</pubDate>
      <link>https://dev.to/danielsogl/the-skill-ai-adoption-actually-requires-leadership-64n</link>
      <guid>https://dev.to/danielsogl/the-skill-ai-adoption-actually-requires-leadership-64n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;AI-assisted software development doesn't just change how fast we write code — it fundamentally changes who we need to be while developing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Over the past months, I've had countless conversations with developers and decision-makers in my role as a consultant and conference speaker. They almost always revolve around the same topics: How much speed does AI actually bring? What data privacy challenges does it create? How secure is AI-generated code?&lt;/p&gt;

&lt;p&gt;All fair questions. But they miss the point that matters most.&lt;/p&gt;

&lt;p&gt;The challenge isn't technical — it's a challenge of &lt;strong&gt;mental models&lt;/strong&gt;. While developers today still primarily &lt;em&gt;write&lt;/em&gt; code, the core competency of tomorrow will be &lt;em&gt;leading&lt;/em&gt; agents. Not real employees. But the parallels to team leadership are closer than most people think.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Social Media Gets Wrong
&lt;/h2&gt;

&lt;p&gt;AI coding tools are improving at a pace that's hard to keep up with — weekly model updates, new agent frameworks, multi-agent workflows becoming the norm. The productivity numbers sound impressive.&lt;/p&gt;

&lt;p&gt;The data tells a different story: A &lt;a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/" rel="noopener noreferrer"&gt;study by METR (2025)&lt;/a&gt; with experienced open-source developers found that in a controlled RCT, developers using AI tools were on average &lt;strong&gt;19% slower&lt;/strong&gt; than without — even though they believed they were 20% faster. METR's &lt;a href="https://metr.org/blog/2026-02-24-uplift-update/" rel="noopener noreferrer"&gt;February 2026 follow-up&lt;/a&gt; notes that models have improved since — and that developers in the follow-up study increasingly refused to work &lt;em&gt;without&lt;/em&gt; AI. The tools are deeply embedded in the workflow, whether productive or not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxekyr5qkf20u6g25i6mj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxekyr5qkf20u6g25i6mj.png" alt="METR Study: Forecasted vs. Actual Developer Productivity with AI Tools" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://shiftmag.dev/this-cto-says-93-of-developers-use-ai-but-productivity-is-still-10-8013/" rel="noopener noreferrer"&gt;DX analysis (February 2026)&lt;/a&gt; based on 4.2 million developers sharpens the picture: 92.6% use AI coding assistants, nearly 27% of production code is already AI-written — and yet measurable productivity gains sit at just &lt;strong&gt;~10%&lt;/strong&gt;. The reason: developers spend only 20–40% of their time actually coding. The rest is problem analysis, product strategy, reviews, communication. A speedup in coding alone barely moves the needle.&lt;/p&gt;

&lt;p&gt;The trust problem compounds this. The &lt;a href="https://survey.stackoverflow.co/2025/ai" rel="noopener noreferrer"&gt;Stack Overflow Developer Survey 2025&lt;/a&gt; (49,000+ developers, 166 countries): &lt;strong&gt;More developers actively distrust AI output (46%) than trust it (33%)&lt;/strong&gt;, with only 3% saying they highly trust AI results. Positive sentiment dropped from 70%+ (2023/2024) to &lt;strong&gt;60%&lt;/strong&gt; — despite rising adoption. 66% struggle with solutions that are &lt;em&gt;almost&lt;/em&gt; right — just wrong enough to make debugging expensive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzpgmgosew366f54i9mx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzpgmgosew366f54i9mx.png" alt="Stack Overflow 2025: AI Tool Sentiment and Usage" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;None of this is an argument against AI coding. It's an argument for doing it &lt;em&gt;right&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Leadership Analogy
&lt;/h2&gt;

&lt;p&gt;Think about what a team lead actually does.&lt;/p&gt;

&lt;p&gt;Their job isn't to make every decision or review every line of code. A good lead &lt;strong&gt;enables&lt;/strong&gt; their team to work autonomously: to make decisions, learn from mistakes, try new approaches. Each role brings specific knowledge. The team moves toward a shared goal.&lt;/p&gt;

&lt;p&gt;Micro-management inverts this — and makes teams slow, demotivated, and dependent.&lt;/p&gt;

&lt;p&gt;This is exactly the pattern many developers unconsciously apply to their AI agents today. Monitor every output, correct every step, distrust by default. Understandable — but it doesn't scale.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://link.springer.com/article/10.1007/s10664-024-10512-1" rel="noopener noreferrer"&gt;Research on software teams shows&lt;/a&gt;: psychological safety is the strongest predictor of team performance across all four DORA metrics. Teams with decision autonomy and a healthy error culture deliver better results. Working with agents is no different.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Agent Leadership Means
&lt;/h2&gt;

&lt;p&gt;The shift developers need to make has three dimensions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. From Writing to Directing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://survey.stackoverflow.co/2025/ai" rel="noopener noreferrer"&gt;Stack Overflow Survey 2025&lt;/a&gt;: 75% of developers would still ask a human when they don't trust the AI's output — even in a future where AI handles most coding tasks. The human isn't an optional backup. They're the critical filter.&lt;/p&gt;

&lt;p&gt;An agent isn't an autopilot. It's a junior developer with broad knowledge but poor judgment in complex situations. &lt;a href="https://scale.com/leaderboard/swe_bench_pro_public" rel="noopener noreferrer"&gt;The best coding agents today only solve ~23% of realistic software tasks&lt;/a&gt; correctly on their own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Calibrating Trust — Neither Blind Faith Nor Blind Rejection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Google DeepMind describes a concrete paradox in a recent paper on &lt;a href="https://arxiv.org/abs/2602.11865" rel="noopener noreferrer"&gt;Intelligent AI Delegation&lt;/a&gt;: when AI takes over too many tasks, humans lose the ability to intervene in critical situations. The fix: deliberately built-in checkpoints where humans retain control.&lt;/p&gt;

&lt;p&gt;Not a weakness in the system — that's design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Establishing a Culture of Failure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Agents make mistakes. Always. The question isn't whether, but how quickly you catch and correct them. DX data shows the difference in practice: companies with strong AI governance experience &lt;strong&gt;50% fewer&lt;/strong&gt; customer-facing incidents — poorly structured teams see &lt;strong&gt;twice as many&lt;/strong&gt;. Governance isn't bureaucracy. It's the difference between AI as a force multiplier and AI as a liability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Trust Through Structure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Automated tests are the equivalent of structured processes in a team.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A team lead doesn't hand over freedom and hope for the best. They create frameworks: clear goals, reviews, feedback loops. For AI agents, automated tests play exactly this role — the safety net that makes autonomy possible.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://arxiv.org/abs/2211.03622" rel="noopener noreferrer"&gt;Stanford study by Perry et al. (2023)&lt;/a&gt;: developers using AI assistants write significantly less secure code — while being convinced they wrote secure code. The Stack Overflow Survey 2025 confirms this at scale: 45% say debugging AI-generated code takes &lt;em&gt;longer&lt;/em&gt; than writing it themselves. Without tests, you're handing an agent full authority without accountability.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Skillset That Actually Matters
&lt;/h2&gt;

&lt;p&gt;In a workshop last week, I asked a group of experienced developers to delegate a real task to a coding agent — the way they'd explain it to a new teammate. Most wrote a one-line prompt and waited. The agent returned code that compiled but solved the wrong problem.&lt;/p&gt;

&lt;p&gt;That's not an agent problem. That's a delegation problem.&lt;/p&gt;

&lt;p&gt;A new developer needs context: Which architectural decisions apply here? What's in scope? What should they &lt;em&gt;not&lt;/em&gt; touch? An agent needs the same information — only more precisely stated, because it won't ask when something's unclear. It interprets and acts.&lt;/p&gt;

&lt;p&gt;Once you internalize that, your approach shifts: tasks get specified more clearly. Acceptance criteria are defined before work starts, not after the first failure. Tests become the language through which you tell an agent what "done" means — not as a post-check, but as a briefing.&lt;/p&gt;

&lt;p&gt;That's the competency shift: from executing to specifying. From writing to leading. Whoever delegates well today — to humans or agents — has a structural advantage that grows with every model update, not shrinks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The conversation around AI-assisted development focuses too much on productivity and too little on the human side of this transformation. Agents are getting better, more autonomous, faster. But the human in this system isn't becoming redundant — they're changing roles.&lt;/p&gt;

&lt;p&gt;From someone who writes code, to someone who sets direction, ensures quality, and enables a system of agents to work together toward a goal.&lt;/p&gt;

&lt;p&gt;Leadership has always been one of the hardest disciplines in software development. In a world of agentic AI, it becomes the core competency.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Daniel is Principal Consultant for Generative AI at Thinktecture AG, Microsoft MVP for Developer Technologies, and a regular speaker at conferences across Germany and Europe.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>Spec Driven Development (SDD) - A initial review</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Tue, 16 Sep 2025 17:54:35 +0000</pubDate>
      <link>https://dev.to/danielsogl/spec-driven-development-sdd-a-initial-review-2llp</link>
      <guid>https://dev.to/danielsogl/spec-driven-development-sdd-a-initial-review-2llp</guid>
      <description>&lt;p&gt;Since "vibe coding" emerged in early 2025 - coined b&lt;a href="https://x.com/karpathy/status/1886192184808149383" rel="noopener noreferrer"&gt;y Andrej Karpathy on February 2nd&lt;/a&gt; - developers have been able to create full applications through AI-assisted coding tools. The numbers are staggering: 25% of Y Combinator's Winter 2025 cohort have codebases that are 95% AI-generated. Initially, there was a boom in creating applications by typing basic prompts into chat windows. While the results looked impressive - though often generic and built on the same UI libraries - developers grew frustrated over time.&lt;/p&gt;

&lt;p&gt;LLMs excel at generating new content, but they often fall short on code quality, architecture choices, API patterns, and security standards. This isn't because LLMs lack capability compared to professional developers, but because developers struggle to define exactly what they want in their prompts, how the LLM should achieve the goal, and how results should be validated. It turns out that while developers are efficient at writing code, they often fail to formulate exactly what they want in plain text.&lt;/p&gt;

&lt;p&gt;When I started learning software development in 2012, I encountered Test Driven Development (TDD), where developers define tests with expected results before writing actual code. I never fully adopted this technique in my development work, though I understood its merits. I found it impractical for daily work because for me, writing software is a creative process that evolves over time. While I consider architecture and patterns before starting, I typically begin coding and refactor later after implementing the requested feature. Writing tests beforehand would certainly help create better structured code from the start, but it would also slow down the creative process.&lt;/p&gt;

&lt;p&gt;However, I once worked for a company developing a complex product where I learned the importance of defining user stories precisely, including personas and acceptance criteria. I often found that stories didn't fully reflect what the product owner wanted me to implement, causing multiple iterations of coding, QA feedback, and adjustments.&lt;/p&gt;

&lt;p&gt;With these lessons from the past and current frustrations with AI-assisted coding tools, there's now a promising pattern that merges the concepts of TDD and well-defined user stories while still allowing creativity in coding - with AI tools handling the heavy lifting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to Spec Driven Development
&lt;/h2&gt;

&lt;p&gt;When we examine what "Vibe Coding" truly means, we realize it lets us forget that code even exists. The main idea is simple: instead of defining goals as code, we express them in plain, natural language. Thanks to LLMs, even people without deep technical knowledge can bring their ideas to life. However, this approach typically works only for prototypes. Production-ready code shouldn't just look cool - it needs to be bug-free, architecturally sound, secure, and functional. To achieve this, developers must define custom instructions with various rules, add MCP servers, and write precise prompts that provide clear guardrails for the LLM. Unfortunately, developers often underestimate the effort required for this setup and prompt engineering.&lt;br&gt;
This is where Spec Driven Development (SDD) comes in. Instead of coding first and documenting later, SDD begins with defining a specification. This specification serves as a contract for how your code should behave and becomes the source of truth that your tools and AI agents use to generate, test, and validate code. The result is less guesswork, fewer surprises, and higher-quality code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Spec Driven Development Phases
&lt;/h2&gt;

&lt;p&gt;SDD divides development into four distinct phases, each with specific goals and outcomes. Throughout these phases, we rely on our chosen AI coding tool for assistance. Our responsibility is to articulate our goals in natural language. The code that's eventually generated is merely the product of these clear specifications.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Specify
&lt;/h3&gt;

&lt;p&gt;In this phase, you provide a high-level description of what you're building and why, then the coding agent generates a detailed specification. This isn't about technical stacks or application design - it's about user journeys, experiences, and success criteria. You'll define who will use your product, what problem it solves for them, how they'll interact with it, and which outcomes matter most. Think of it as mapping the desired user experience while letting the coding agent elaborate on the details. This specification becomes a living document that evolves as you gain deeper insights into your users and their needs.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Plan
&lt;/h3&gt;

&lt;p&gt;This phase is where you get technical. You'll provide the coding agent with your desired tech stack, architecture, and constraints, and it will generate a comprehensive technical plan. If your company has standardized technologies, specify them here. Detail any integrations with legacy systems, compliance requirements, or performance targets you need to meet. You can also request multiple plan variations to compare different approaches. By making your internal documentation available to the coding agent, it can directly incorporate your architectural patterns and standards into the plan. Think of this as setting the rules before the game begins - the coding agent needs to understand the parameters before it can start working effectively.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Tasks
&lt;/h3&gt;

&lt;p&gt;The coding agent transforms the spec and plan into actionable work by generating small, reviewable chunks that each address a specific component. Each task should be implementable and testable in isolation—this approach allows the coding agent to validate its work and maintain focus, similar to a test-driven development process. Rather than broad directives like "build authentication," you receive precise tasks such as "create a user registration endpoint that validates email format.”&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Implement
&lt;/h3&gt;

&lt;p&gt;The coding agent executes tasks sequentially or in parallel as appropriate. The key difference here is that instead of reviewing massive code dumps, you - the developer - evaluate focused, specific changes that address particular problems. The coding agent has clear direction: the specification defines what to build, the plan outlines how to build it, and the task specifies exactly what to work on.&lt;/p&gt;

&lt;p&gt;Crucially, your role extends beyond steering - you must verify. At each phase, reflect and refine. Does the specification truly capture your intended product? Does the plan address real-world constraints? Has the AI overlooked any edge cases or omissions? The process includes deliberate checkpoints where you evaluate what's been generated, identify gaps, and correct course before proceeding. While the AI generates the artifacts, you ensure their accuracy and relevance.&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction to GitHub Spec Kit
&lt;/h2&gt;

&lt;p&gt;Since following SDD requires defining extensive prose text—including specifications, tech stack details, test cases, and more - GitHub released an open-source CLI called Spec Kit to simplify creating the required files. Released on September 2, 2025, Spec Kit is currently at version 0.0.30+ and continues rapid development with frequent updates and community contributions.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Spec Kit Installation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Spec Kit can be installed in two different ways. The first method uses uvx, a Python package manager that works like npm with npx. I recommend installing uvx through Homebrew.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx &lt;span class="nt"&gt;--from&lt;/span&gt; git+https://github.com/github/spec-kit.git specify init &amp;lt;PROJECT_NAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jjybwmbdhkrolkd685e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jjybwmbdhkrolkd685e.png" alt="Specify screenshot" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This command creates a new folder with the defined name. You'll then confirm which AI coding assistant you want to use. Currently, you can't switch between selected tools after initialization.&lt;/p&gt;

&lt;p&gt;This command creates a new folder with the defined name. You’ll then confirm which AI coding assistant you want to use. Currently, you can’t switch between selected tools after initialization.&lt;/p&gt;

&lt;p&gt;If you want to create the files in an existing project, you can pass the &lt;code&gt;--here&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alternative installation method&lt;/strong&gt;: You can download platform-specific template packages without using the CLI. These are available on the &lt;a href="https://github.com/github/spec-kit/releases" rel="noopener noreferrer"&gt;release page&lt;/a&gt; in both POSIX shell (sh) and PowerShell (ps) variants.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;To demonstrate Spec Kit, I'll create a new project using GitHub Copilot as my AI coding assistant. For my tech stack, I'll use Angular with the latest ESLint, Prettier, and TypeScript APIs. This will also show how to ensure Spec Kit follows the most current APIs of your preferred framework.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbjuoh3pwxi77wq61tlmw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbjuoh3pwxi77wq61tlmw.png" alt="Specify screenshot" width="800" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Spec Kit Files Overview
&lt;/h2&gt;

&lt;p&gt;Based on your selected tool, Spec Kit creates various files in specific directories. Understanding this structure is crucial for effective implementation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjpl88kvv55231yh9cam.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjpl88kvv55231yh9cam.png" alt="Specify screenshot" width="720" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt Files
&lt;/h2&gt;

&lt;p&gt;The prompt folder contains reusable prompts that can be executed through GitHub Copilot. These prompts include instructions that GitHub Copilot should follow. We will use these prompts to create our initial specification, technical implementation plan, and finally, the tasks we can execute step by step. This approach simplifies following Spec Driven Development guidelines, as the GitHub team has precisely defined what the AI agent must do, ensuring it follows the established guardrails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory Directory
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;memory/&lt;/code&gt; folder contains the constitution.md, which defines your principles such as preferred architecture, API exposure patterns, TDD approach, libraries, technologies, and coding standards. You should fill out this file in detail before running any prompts. Your AI coding tool can help with this process. I recommend using context7 MCP or another documentation MCP server to retrieve the latest style guides for your framework. For best results, use the most advanced LLMs available, such as Sonnet 4 or GPT-4.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fill out the project constitution for a modern Angular web application. Ensure it follows Domain-Driven Design, uses TypeScript with Angular Material and SCSS, is built with TDD principles, adheres to Clean Code practices, and complies with the latest Angular Style Guides. #context7 #angular-cli #file:constitution.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scripts Directory
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;scripts/&lt;/code&gt; directory contains shell scripts executed by the coding agent to help it understand specifications, create branches, and generate other required files. You should not modify these files manually, as they're integral to the SDD workflow automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Templates Directory
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;templates/&lt;/code&gt; folder contains markdown templates for files the coding agent will generate later. These ensure consistent formatting across all generated specifications, plans, and tasks. You should avoid modifying these files manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI Tool Integration
&lt;/h3&gt;

&lt;p&gt;Different AI tools use different instruction files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Copilot&lt;/strong&gt;: &lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor&lt;/strong&gt;: &lt;code&gt;.cursorrules&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude&lt;/strong&gt;: &lt;code&gt;CLAUDE.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini&lt;/strong&gt;: &lt;code&gt;GEMINI.md&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Enhanced in v0.0.30+&lt;/strong&gt;: Template system now includes platform-specific variants (POSIX shell and PowerShell) for cross-platform compatibility, plus comprehensive validation to ensure instruction files are properly configured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Your First Specification
&lt;/h2&gt;

&lt;p&gt;After defining the constitution, it's time to create your first specification. This initial step is crucial for defining your application's goal. Be as specific as possible. You can also use your preferred AI to enhance the description. Focus on the user experience rather than technical details.&lt;/p&gt;

&lt;p&gt;To use the specify functionality, execute the command through your chosen AI tool. In GitHub Copilot's case, this involves using the &lt;code&gt;/specify&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/specify NG Pokedex is an application designed for Pokémon fans who want a simple and engaging way to discover and explore their favorite creatures. The app focuses on making the search and discovery process intuitive: users can quickly look up a Pokémon by name or browse by type, such as Fire or Water. Each Pokémon entry presents the essentials—its name, image, and key stats—so users can immediately recognize and compare them.

When users want to dive deeper, they can click on a Pokémon to open a detail view that provides richer information in a focused and easy-to-read format. To personalize the experience, users can mark their favorite Pokémon and curate their own collection, which can be updated at any time by toggling a star icon.

The experience is designed to be seamless across devices—desktop, tablet, and mobile—so users can enjoy discovering and managing their Pokémon wherever they are. Success for this application means that users feel empowered to explore the Pokémon world effortlessly, stay engaged through personalization features like favorites, and return to the app because it makes their interactions simple, fun, and rewarding.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this prompt creates your first spec file and branch. For each specification, the agent creates a separate branch to work on—in my example, &lt;code&gt;001-ng-pokedex-is&lt;/code&gt;. This process may take a few minutes, as the coding agent calls multiple scripts, creates files, and generates user stories, requirements, and key entities. The agent ensures it follows all instructions defined by the Spec Kit CLI.&lt;/p&gt;

&lt;p&gt;Open the generated &lt;code&gt;spec.md&lt;/code&gt; file and scroll through it. At the bottom of the file, the agent validates the following requirements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Content Quality
- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

### Requirement Completeness
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous  
- [x] Success criteria are measurable
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If any of these checkboxes remain unchecked, you should either rerun the specify command with more detailed information or complete the document yourself. It's crucial to validate that the defined stories and requirements align with your vision for the application or feature. Edge cases, for example, need to be defined manually since the agent doesn't specify them automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Edge Cases

- What happens when no Pokémon match a search query? The system MUST display a user-friendly message indicating no results were found and suggest alternative actions (e.g., broadening the search criteria).
- How does the system handle when a user tries to access detailed information for a Pokémon that doesn't exist? The system MUST display a 404 error page with a friendly message and a link back to the main page.
- What occurs when the user has no favorited Pokémon yet? The system MUST display a message indicating that the favorites list is empty and suggest Pokémon to explore.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a Technical Plan
&lt;/h2&gt;

&lt;p&gt;After finalizing the non-technical specification, it's time to create the technical plan for implementation. From my experience, you can optimize this process by establishing the default application structure upfront instead of relying on the coding agent to do so. I suggest using context7 MCP again to ensure you follow the latest project setup guidelines. In my case, I'll also incorporate the Angular CLI MCP server to generate the Angular project. Be prepared for this prompt to take several minutes to complete.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/plan The application will be developed using Angular 20 together with Angular Material, following the guidelines of Material Design v3. There is no backend; instead, all Pokémon data will be retrieved directly from the PokeAPI V2. The list of favorites will be stored in the browser’s local storage, which means the data is tied to the user’s device and will be reloaded whenever the page is refreshed. Testing will be carried out with Jasmine and Karma, both of which are included in the standard Angular CLI setup. No other third-party libraries will be introduced, apart from those provided by the Angular CLI project and Angular Material itself. This approach ensures a lean and maintainable project that strictly follows Angular’s official ecosystem. The architecture will be guided by Angular best practices and style guides, while performance, responsiveness, and accessibility according to Material Design v3 are considered essential requirements. This plan defines the technical foundation and the constraints within which the coding agent will generate the specification, ensuring a consistent and future-proof implementation. #context7 #angular-cli 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this stage, the agent generates several files, including a &lt;code&gt;plan.md&lt;/code&gt; document. This document outlines how the agent should research information needed to create tasks, and includes research results, instructions for following your guidelines, and entity model definitions. Review these files carefully to ensure the agent will adhere to your architecture, framework standards, and specification requirements. Pay particular attention to the &lt;code&gt;quickstart.md&lt;/code&gt; file, as it contains instructions for initializing the project files. Verify that the npm versions match the latest versions of your preferred framework and tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwp0y8goy9iib5gulvtjm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwp0y8goy9iib5gulvtjm.png" alt="Specify screenshot" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining Implementation Tasks
&lt;/h2&gt;

&lt;p&gt;This is the final specification step where Spec Kit helps us create task definitions for our chosen coding agent. This step doesn't require adding any specifications to the prompt file execution. Simply run the &lt;code&gt;/tasks&lt;/code&gt; command in your chat window and wait while the coding agent defines all the tasks needed to implement your well-defined specification. Be patient, as this process can take several minutes to complete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2lq8s4v512hnri5urr6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2lq8s4v512hnri5urr6.png" alt="Specify screenshot" width="800" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After executing the tasks prompt, the agent creates a &lt;code&gt;tasks.md&lt;/code&gt; file, including a step-by-step definition of all tasks that need to be executed to create your defined specification. Again, review the file manually to make sure everything is defined and no todos are open at the end of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Validation Checklist
**GATE: All requirements met for execution**

- [✅] All 3 contracts have corresponding tests (T006-T008)
- [✅] All 9 entities have model tasks (T015-T023)  
- [✅] All tests come before implementation (T006-T014 before T024+)
- [✅] Parallel tasks truly independent (different files, no shared state)
- [✅] Each task specifies exact file path with Angular conventions
- [✅] No task modifies same file as another [P] task
- [✅] Constitutional compliance: Standalone components, signals, Material Design v3
- [✅] Domain boundaries respected: Pokemon, Favorites, Search
- [✅] Angular 20 modern patterns enforced throughout

**Total Tasks**: 44 tasks organized in 5 phases with clear dependencies
**Estimated Duration**: 3-4 weeks for full implementation with testing
**Parallel Opportunities**: 28 tasks marked [P] for parallel execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tasks are organized into different phases based on the specification's complexity. In my example, there are five phases: Setup, TDD, Core Implementation, Integration, and Polish. Each task has a unique identifier (e.g., T001) that you can reference when instructing your agent which task to complete first. It's essential to follow the phases and tasks in their intended order. If you complete a task manually, be sure to mark it as completed in the tasks file.&lt;br&gt;
Now it's time for your coding agent to do the hard work for you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Limitations and Considerations
&lt;/h2&gt;

&lt;p&gt;As of September 2025, Spec Kit is still experimental (version 0.0.30+) and has some known limitations worth considering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tool Switching&lt;/strong&gt;: Once initialized with a specific AI tool, you cannot easily switch to another&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Greenfield Focus&lt;/strong&gt;: Better suited for new projects than existing codebases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python Dependency&lt;/strong&gt;: Requires Python 3.11+ for uvx installation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rapid development&lt;/strong&gt;: Features and structure change frequently; documentation may lag behind latest capabilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community-driven expansion&lt;/strong&gt;: Many new AI tool integrations come from community pull requests, creating inconsistent quality&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Road Ahead
&lt;/h2&gt;

&lt;p&gt;GitHub has positioned Spec Kit as an experiment in structured AI-assisted development. Early adoption statistics show promise - major tech companies are already incorporating SDD principles into their development workflows, and the tool has gained traction among developers frustrated with unstructured AI coding approaches.&lt;/p&gt;

&lt;p&gt;The integration ecosystem continues expanding. Recent updates to GitHub Copilot (including the new Coding Agent in public preview) and competitive responses from Cursor and other AI coding tools suggest that specification-driven approaches will become standard practice rather than experimental techniques.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Spec Driven Development promises a structured approach to AI-assisted coding that addresses the quality and maintainability issues inherent in "vibe coding." GitHub Spec Kit simplifies this process through predefined prompts and templates, providing developers with guardrails for AI code generation.&lt;/p&gt;

&lt;p&gt;In theory, SDD appears to be the logical evolution of today's AI-assisted development, but in practice, it currently faces the fundamental challenge of human specification. The main problem isn't the AI - it's the human factor. SDD requires developers to specify their intentions precisely, which AI agents will ultimately execute. Yet this is exactly where the model faces its greatest challenge.&lt;/p&gt;

&lt;p&gt;After over 10 years in software development, I have rarely experienced projects where requirements were completely formulated before implementation—neither by product developers nor by developers themselves. In my talks, I jokingly refer to us developers as "efficient," but "lazy" would probably be the more honest term.&lt;/p&gt;

&lt;p&gt;Spec Kit encourages developers to continuously expand the generated documents and refine specifications. However, based on my examples from recent weeks, this approach doesn't automatically lead to better results. Only through the use of additional rules and MCP servers was I able to have my requirements - using GitHub Copilot - implemented cleanly and completely.&lt;/p&gt;

&lt;p&gt;The question remains whether this model actually reduces development time or whether addressing a self-created problem - managing AI-generated code quality - leads to more frustration in the long term due to poorly optimized code architecture and increased specification overhead.&lt;/p&gt;

&lt;p&gt;I will continue experimenting with Spec Driven Development in my projects and publish updates as the methodology and tooling mature. The early indicators suggest that while SDD adds upfront specification overhead, it may prove essential for scaling AI-assisted development beyond prototype-level applications.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Gemini Code Assist for GitHub: Automated Code Reviews with Gemini</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Sun, 06 Apr 2025 11:11:31 +0000</pubDate>
      <link>https://dev.to/danielsogl/gemini-code-assist-for-github-automated-code-reviews-with-gemini-3km5</link>
      <guid>https://dev.to/danielsogl/gemini-code-assist-for-github-automated-code-reviews-with-gemini-3km5</guid>
      <description>&lt;p&gt;As a solo developer working on my side project &lt;strong&gt;codingrules.ai&lt;/strong&gt;, I needed an efficient way to perform code reviews without another set of eyes. Google's Gemini Code Assist emerged as an ideal solution. In this post, I'll explore how Gemini Code Assist helps automate pull request reviews, detail its key features, and show how you can tailor its behavior specifically for your GitHub repositories.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Gemini Code Assist for GitHub?
&lt;/h2&gt;

&lt;p&gt;Gemini Code Assist is Google's AI-powered code review assistant, seamlessly integrated with GitHub. Built upon Google's advanced Gemini large language models—fine-tuned with billions of lines of open-source code—Gemini automates the review of pull requests, providing intelligent, context-aware feedback to improve your code quality and streamline your workflow.&lt;/p&gt;

&lt;p&gt;Gemini Code Assist offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automated reviews identifying potential issues&lt;/li&gt;
&lt;li&gt;Contextual, actionable feedback&lt;/li&gt;
&lt;li&gt;Support for coding best practices and language-specific patterns&lt;/li&gt;
&lt;li&gt;Customizable review behavior aligned with your team's standards&lt;/li&gt;
&lt;li&gt;Easy integration with GitHub Actions for workflow automation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Features of Gemini Code Assist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Automated Pull Request Reviews
&lt;/h3&gt;

&lt;p&gt;Gemini automatically analyzes your pull requests, identifying potential bugs, performance issues, and adherence to coding standards—all without manual intervention.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxmepehp8xlo8e4m235e0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxmepehp8xlo8e4m235e0.png" alt="Pull Request Summary Screenshot" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Standard Review Patterns
&lt;/h3&gt;

&lt;p&gt;By default, Gemini reviews code in five primary areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Correctness:&lt;/strong&gt; Checks logic errors, race conditions, edge cases, and incorrect API usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficiency:&lt;/strong&gt; Highlights performance issues such as memory leaks, inefficient loops, redundant computations, and inefficient string manipulation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability:&lt;/strong&gt; Reviews readability, modularity, naming conventions, documentation, complexity, duplication, formatting, and magic numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; Identifies vulnerabilities like insecure data handling, injection attacks, insufficient access controls, and insecure direct object references.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Miscellaneous:&lt;/strong&gt; Addresses testing, scalability, modularity, reusability, and error logging/monitoring.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Code Context Awareness
&lt;/h3&gt;

&lt;p&gt;Gemini considers the broader context of your codebase, ensuring relevant, precise feedback tailored specifically to your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. GitHub Actions Integration
&lt;/h3&gt;

&lt;p&gt;Easily automate code reviews with GitHub Actions, triggering Gemini reviews on every pull request automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Multiple Feedback Formats
&lt;/h3&gt;

&lt;p&gt;Gemini provides inline comments, summary reports, and suggested fixes, ensuring clear and actionable feedback.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Gemini Code Assist Reviews Pull Requests
&lt;/h2&gt;

&lt;p&gt;Gemini Code Assist simplifies and enhances the traditional pull request review process:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Pull Request Summary
&lt;/h3&gt;

&lt;p&gt;Once the pull request is created, Gemini Code Assist will generate a summary comment. This comment includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A brief overview of the changes made in the pull request&lt;/li&gt;
&lt;li&gt;Highlights of significant modifications&lt;/li&gt;
&lt;li&gt;A changelog detailing the specific changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw3q4ep7g3ikv3ocxcx6y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw3q4ep7g3ikv3ocxcx6y.png" alt="Pull Request Summary Screenshot" width="800" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Code Review Findings Summary
&lt;/h3&gt;

&lt;p&gt;After the initial summary, Gemini will provide a second comment summarizing the code review findings. This summary includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An overview of the issues detected&lt;/li&gt;
&lt;li&gt;The severity and impact of each issue&lt;/li&gt;
&lt;li&gt;Suggestions for improvement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50dxbil1eezojnu31q6c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50dxbil1eezojnu31q6c.png" alt="Code Review Findings Summary Screenshot" width="800" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Detailed Code Reviews
&lt;/h3&gt;

&lt;p&gt;Finally, Gemini Code Assist will create detailed code reviews for each finding. Each review includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The priority of the finding (e.g., critical, high, medium, low)&lt;/li&gt;
&lt;li&gt;An example of what should be changed&lt;/li&gt;
&lt;li&gt;A reference to the style guide rule that triggered the review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fksg3omm0u6gdqrxefcrh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fksg3omm0u6gdqrxefcrh.png" alt="Detailed Code Review Screenshot" width="800" height="758"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This structured approach ensures thorough reviews, maintaining high code quality standards across your repository.&lt;/p&gt;




&lt;h2&gt;
  
  
  Customizing Reviews with the &lt;code&gt;.gemini&lt;/code&gt; Directory
&lt;/h2&gt;

&lt;p&gt;Gemini Code Assist allows extensive customization using a special &lt;code&gt;.gemini&lt;/code&gt; directory in your repository. This directory helps you standardize your coding practices across your team or personal projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the &lt;code&gt;.gemini&lt;/code&gt; Directory
&lt;/h3&gt;

&lt;p&gt;To customize Gemini's behavior:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a &lt;code&gt;.gemini&lt;/code&gt; directory&lt;/strong&gt; in the base of your repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add &lt;code&gt;config.yaml&lt;/code&gt;&lt;/strong&gt; to define Gemini’s behavior and review criteria.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optionally, add a &lt;code&gt;styleguide.md&lt;/code&gt;&lt;/strong&gt; to detail your project's coding standards explicitly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Example Directory Structure
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.gemini/
├── config.yaml      # Gemini review configuration
└── styleguide.md    # Custom coding style guide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example &lt;code&gt;config.yaml&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="na"&gt;enabled_features&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;auto_review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;post_review_summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;inline_suggestions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;review_pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;severity_level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;review_comment_severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;critical&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;high&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;medium&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;low&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="na"&gt;code_review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;disable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;comment_severity_threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MEDIUM&lt;/span&gt;
  &lt;span class="na"&gt;max_review_comments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-1&lt;/span&gt;

&lt;span class="na"&gt;pull_request_opened&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;code_review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration enables detailed automatic reviews, inline suggestions, and summary reports, filtering out lower-severity comments if desired.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example &lt;code&gt;styleguide.md&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Project Style Guide for codingrules.ai&lt;/span&gt;

Our project utilizes:
&lt;span class="p"&gt;
-&lt;/span&gt; Next.js 15
&lt;span class="p"&gt;-&lt;/span&gt; React 19
&lt;span class="p"&gt;-&lt;/span&gt; TypeScript
&lt;span class="p"&gt;-&lt;/span&gt; Tailwind CSS v4
&lt;span class="p"&gt;-&lt;/span&gt; Supabase
&lt;span class="p"&gt;-&lt;/span&gt; shadcn UI

&lt;span class="gu"&gt;## General Coding Standards&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Use TypeScript exclusively, avoiding &lt;span class="sb"&gt;`any`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Favor functional and React Server Components.
&lt;span class="p"&gt;-&lt;/span&gt; Implement robust error boundaries and accessibility practices (a11y).
&lt;span class="p"&gt;-&lt;/span&gt; Apply proper internationalization (i18n) techniques.

&lt;span class="gu"&gt;## React &amp;amp; TypeScript Best Practices&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Explicit typing with interfaces.
&lt;span class="p"&gt;-&lt;/span&gt; Limit type assertions and unnecessary annotations.
&lt;span class="p"&gt;-&lt;/span&gt; Validate types at runtime with Zod.
&lt;span class="p"&gt;-&lt;/span&gt; Prioritize hooks for state management and side effects.

&lt;span class="gu"&gt;## Supabase Integration&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Separate Supabase clients for server and client components.
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`@supabase/ssr`&lt;/span&gt; for server-side rendering.
&lt;span class="p"&gt;-&lt;/span&gt; Properly configure environment variables and client initialization patterns.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Getting Started with Gemini Code Assist
&lt;/h2&gt;

&lt;p&gt;Setting up Gemini Code Assist on GitHub is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install&lt;/strong&gt; Gemini Code Assist from the GitHub Marketplace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorize&lt;/strong&gt; the app for your GitHub account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select repositories&lt;/strong&gt; for Gemini reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a &lt;code&gt;.gemini&lt;/code&gt; directory&lt;/strong&gt; for custom configurations (optional but recommended).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrate with GitHub Actions&lt;/strong&gt; to automate pull request reviews.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Future Outlook
&lt;/h2&gt;

&lt;p&gt;As AI-driven code reviews evolve, Gemini Code Assist is well-positioned to further improve development workflows. Anticipated enhancements include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More advanced code analysis techniques&lt;/li&gt;
&lt;li&gt;Greater understanding of complex coding patterns&lt;/li&gt;
&lt;li&gt;Enhanced customization capabilities&lt;/li&gt;
&lt;li&gt;Improved integration with GitHub collaboration tools&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Gemini Code Assist for GitHub provides powerful AI-driven code reviews, customized through the &lt;code&gt;.gemini&lt;/code&gt; directory. For solo developers or teams, it's an invaluable tool that ensures code quality, consistency, and faster integration cycles. Adopting Gemini Code Assist transforms the review process, making it simpler, quicker, and more effective.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Ready to try Gemini Code Assist for your project?&lt;/strong&gt; Head to the &lt;a href="https://github.com/marketplace" rel="noopener noreferrer"&gt;GitHub Marketplace&lt;/a&gt; and streamline your code reviews today.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>gemini</category>
      <category>github</category>
      <category>ai</category>
    </item>
    <item>
      <title>Generating and Storing Google Gemini Embeddings with Vercel AI SDK and Supabase</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Tue, 01 Apr 2025 17:17:55 +0000</pubDate>
      <link>https://dev.to/danielsogl/generating-and-storing-google-gemini-embeddings-with-vercel-ai-sdk-and-supabase-283d</link>
      <guid>https://dev.to/danielsogl/generating-and-storing-google-gemini-embeddings-with-vercel-ai-sdk-and-supabase-283d</guid>
      <description>&lt;p&gt;Text embeddings are numerical vectors representing concepts like text, enabling AI tasks such as semantic search, recommendations, and clustering. This post guides you through generating text embeddings using &lt;a href="https://deepmind.google/technologies/gemini/" rel="noopener noreferrer"&gt;Google Gemini&lt;/a&gt; via the &lt;a href="https://sdk.vercel.ai/" rel="noopener noreferrer"&gt;Vercel AI SDK&lt;/a&gt; and storing them in a &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; Postgres database with the &lt;code&gt;pgvector&lt;/code&gt; extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Vercel AI SDK and Supabase?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vercel AI SDK:&lt;/strong&gt; Offers a unified, developer-friendly API for interacting with AI providers like Google, simplifying embedding generation. &lt;a href="https://sdk.vercel.ai/docs" rel="noopener noreferrer"&gt;Source: Vercel AI SDK Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase &amp;amp; pgvector:&lt;/strong&gt; Provides a scalable Postgres database with &lt;code&gt;pgvector&lt;/code&gt; for efficient vector storage and similarity search within the database. &lt;a href="https://supabase.com/docs/guides/ai" rel="noopener noreferrer"&gt;Source: Supabase AI Docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js environment (LTS recommended)&lt;/li&gt;
&lt;li&gt;A Supabase project (&lt;a href="https://supabase.com/dashboard" rel="noopener noreferrer"&gt;Create one&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;A Google AI API Key (&lt;a href="https://aistudio.google.com/app/apikey" rel="noopener noreferrer"&gt;Get one via Google AI Studio&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;A Vercel account (Optional, for easy deployment)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Setup
&lt;/h2&gt;

&lt;p&gt;Install the necessary packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add ai @ai-sdk/google @supabase/supabase-js
&lt;span class="c"&gt;# or&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;ai @ai-sdk/google @supabase/supabase-js
&lt;span class="c"&gt;# or&lt;/span&gt;
yarn add ai @ai-sdk/google @supabase/supabase-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;.env.local&lt;/code&gt; file for your environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Required for Vercel AI SDK (Google)
GOOGLE_API_KEY='your-google-ai-api-key'

# Required for Supabase client
NEXT_PUBLIC_SUPABASE_URL='your-supabase-project-url'
# Public key for client-side reads (e.g., semantic search)
NEXT_PUBLIC_SUPABASE_ANON_KEY='your-supabase-anon-key'
# Service role key for server-side writes (e.g., storing embeddings)
SUPABASE_SERVICE_ROLE_KEY='your-supabase-service-role-key'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt;&lt;br&gt;
Protect your API keys. Add &lt;code&gt;.env.local&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; and use environment variables in your deployment environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Initialize the Supabase client. Create separate clients for server-side operations (using the service role key) and client-side operations (using the anon key) if needed, or manage access appropriately based on your architecture.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@supabase/supabase-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabaseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_SUPABASE_URL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabaseAnonKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_SUPABASE_ANON_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabaseServiceRoleKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SUPABASE_SERVICE_ROLE_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Client for public read access (e.g., in browser or Edge Functions)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;supabaseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;supabaseAnonKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Client for server-side operations requiring elevated privileges&lt;/span&gt;
&lt;span class="c1"&gt;// Use this cautiously and only in secure server environments&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabaseAdmin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;supabaseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;supabaseServiceRoleKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Prepare Supabase Database
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Enable &lt;code&gt;vector&lt;/code&gt; extension:&lt;/strong&gt; In your Supabase Dashboard, navigate to &lt;code&gt;Database&lt;/code&gt; &amp;gt; &lt;code&gt;Extensions&lt;/code&gt;, find &lt;code&gt;vector&lt;/code&gt;, and enable it.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Create table:&lt;/strong&gt; Go to the &lt;code&gt;SQL Editor&lt;/code&gt; and run the SQL below. &lt;strong&gt;Crucially, adjust &lt;code&gt;VECTOR(768)&lt;/code&gt;&lt;/strong&gt; if you plan to use a different Gemini model or optimize/truncate embeddings to a different dimension. &lt;code&gt;text-embedding-004&lt;/code&gt; uses 768 dimensions. &lt;a href="https://ai.google.dev/docs/embeddings" rel="noopener noreferrer"&gt;Source: Google AI Embeddings&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Ensure the vector extension is enabled&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Create table to store documents and their embeddings&lt;/span&gt;
&lt;span class="c1"&gt;-- IMPORTANT: Adjust VECTOR dimensions based on your model and optimization strategy&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;BIGSERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                       &lt;span class="c1"&gt;-- The original text content&lt;/span&gt;
  &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;               &lt;span class="c1"&gt;-- Use 768 for text-embedding-004 (default Gemini model)&lt;/span&gt;
                                      &lt;span class="c1"&gt;-- Use a smaller value (e.g., 256) if truncating&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Optional: Create an index for faster similarity search&lt;/span&gt;
&lt;span class="c1"&gt;-- Choose one index type based on your needs:&lt;/span&gt;

&lt;span class="c1"&gt;-- HNSW: Good balance of speed and recall (recommended for many use cases)&lt;/span&gt;
&lt;span class="c1"&gt;-- Adjust m and ef_construction based on dataset size and desired recall/speed trade-off&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;hnsw&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;vector_cosine_ops&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;-- WITH (m = 16, ef_construction = 64); -- Example parameters&lt;/span&gt;

&lt;span class="c1"&gt;-- IVFFlat: Faster builds, potentially lower recall than HNSW&lt;/span&gt;
&lt;span class="c1"&gt;-- Adjust 'lists' based on your dataset size (e.g., sqrt(N) where N is # rows)&lt;/span&gt;
&lt;span class="c1"&gt;-- CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops)&lt;/span&gt;
&lt;span class="c1"&gt;-- WITH (lists = 100); -- Example parameter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Create Search Function:&lt;/strong&gt; Still in the SQL Editor, create a stored procedure for efficient similarity search. Again, ensure &lt;code&gt;VECTOR(768)&lt;/code&gt; matches the dimension used in your &lt;code&gt;documents&lt;/code&gt; table.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Function to search for similar documents using cosine similarity&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;match_documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;query_embedding&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;-- Match the vector dimension in your table&lt;/span&gt;
  &lt;span class="n"&gt;match_threshold&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;-- Similarity threshold (e.g., 0.7)&lt;/span&gt;
  &lt;span class="n"&gt;match_count&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;               &lt;span class="c1"&gt;-- Max number of results to return&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;similarity&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;QUERY&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;query_embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;similarity&lt;/span&gt; &lt;span class="c1"&gt;-- Cosine similarity&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;query_embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;match_threshold&lt;/span&gt;
  &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;query_embedding&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt; &lt;span class="c1"&gt;-- Order by distance (closest first)&lt;/span&gt;
  &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="n"&gt;match_count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Generate, Optimize (Optional), and Store Embeddings
&lt;/h2&gt;

&lt;p&gt;Create functions to generate, optionally optimize, and store embeddings in Supabase. We'll put these in &lt;code&gt;lib/embeddingUtils.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Configuration and Imports&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, import necessary modules and define configuration constants for the embedding model and dimensions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;google&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ai-sdk/google&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;embed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;supabaseAdmin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./supabaseClient&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Use admin client for inserts&lt;/span&gt;

&lt;span class="c1"&gt;// Choose your Google embedding model (e.g., text-embedding-004)&lt;/span&gt;
&lt;span class="c1"&gt;// Ref: https://ai.google.dev/docs/embeddings#available_models&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EMBEDDING_MODEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;textEmbeddingModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-embedding-004&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Define the raw dimension of the chosen model&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RAW_VECTOR_DIMENSIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Define the target dimension for optimization (if used). Must match DB schema!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OPTIMIZED_VECTOR_DIMENSIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Example: optimize to 256 dimensions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Helper Function: L2 Normalization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This helper function normalizes a vector (scales it to have a length of 1), which is often beneficial for cosine similarity calculations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... imports and config from above&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalizeL2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;norm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;norm&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Avoid division by zero&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Helper Function: Optimize Embedding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This function optionally truncates an embedding to a target dimension and then normalizes it using the &lt;code&gt;normalizeL2&lt;/code&gt; helper.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... imports, config, and normalizeL2 from above&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Truncates (optional) and normalizes an embedding vector.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;optimizeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="nx"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Default: no truncation&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dimension&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dimension&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Invalid target dimension &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Using original length &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;dimension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. Truncate if necessary&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;truncated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;dimension&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Normalize&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeL2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;truncated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Optimization resulted in unexpected dimension: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Core Function: Embed and Store&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This main function orchestrates the process: it takes text, generates the embedding using the Vercel AI SDK, calls the optimization helper if requested, and inserts the original text and final embedding vector into the Supabase table using the admin client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... imports, config, and helpers from above&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;embedAndStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;optimize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Set to true to truncate and normalize&lt;/span&gt;
  &lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;documents&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Make table name configurable&lt;/span&gt;
  &lt;span class="nx"&gt;contentColumn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;embeddingColumn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;embedding&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanedText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Google models handle newlines generally well&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Generating Gemini embedding for: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cleanedText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;..."`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Generate the initial embedding&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rawEmbedding&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EMBEDDING_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cleanedText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// Optional: Use Google's built-in dimensionality reduction&lt;/span&gt;
      &lt;span class="c1"&gt;// ...(optimize ? { parameters: { outputDimensionality: OPTIMIZED_VECTOR_DIMENSIONS } } : {}),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Basic validation of raw embedding length&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;rawEmbedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;RAW_VECTOR_DIMENSIONS&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;optimize&lt;/span&gt; &lt;span class="cm"&gt;/* Add check if using Google's param */&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Expected &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;RAW_VECTOR_DIMENSIONS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; dimensions, got &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rawEmbedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Optimize (truncate/normalize) or just normalize&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalEmbedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;optimize&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;optimizeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawEmbedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OPTIMIZED_VECTOR_DIMENSIONS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;optimizeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawEmbedding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Normalize only if not truncating&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate final embedding length&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetDimension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;optimize&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;OPTIMIZED_VECTOR_DIMENSIONS&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RAW_VECTOR_DIMENSIONS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;finalEmbedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;targetDimension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Final embedding dimension mismatch: expected &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetDimension&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, got &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;finalEmbedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Store in Supabase&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Storing embedding with &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;finalEmbedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; dimensions.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabaseAdmin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;contentColumn&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;cleanedText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;embeddingColumn&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;finalEmbedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Supabase insert error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Failed to store embedding in Supabase: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Successfully stored text and embedding.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error in embedAndStore process:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* Example Usage Placeholder: Add example API route/Server Action call here if needed */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Optimization Considerations:&lt;/strong&gt;&lt;br&gt;
If you enable optimization (truncation + normalization), ensure the &lt;code&gt;VECTOR(...)&lt;/code&gt; dimension in your &lt;code&gt;documents&lt;/code&gt; table and &lt;code&gt;match_documents&lt;/code&gt; function &lt;strong&gt;exactly matches&lt;/strong&gt; &lt;code&gt;OPTIMIZED_VECTOR_DIMENSIONS&lt;/code&gt; (e.g., 256). If optimization is disabled, ensure they match &lt;code&gt;RAW_VECTOR_DIMENSIONS&lt;/code&gt; (768 for &lt;code&gt;text-embedding-004&lt;/code&gt;). Consistency is key! Normalization (&lt;code&gt;normalizeL2&lt;/code&gt;) is generally beneficial for cosine similarity searches even without truncation. Google's models also support an &lt;code&gt;outputDimensionality&lt;/code&gt; parameter in the API call for built-in reduction. &lt;a href="https://sdk.vercel.ai/providers/ai-sdk-providers/google-generative-ai" rel="noopener noreferrer"&gt;Source: Vercel AI SDK - Google Provider&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 4: Implement Semantic Search
&lt;/h2&gt;

&lt;p&gt;Create a function in &lt;code&gt;lib/semanticSearch.ts&lt;/code&gt; to perform semantic search using the stored embeddings and the Supabase RPC function (&lt;code&gt;match_documents&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Configuration and Imports&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Import dependencies and define configuration matching the embedding generation step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;google&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ai-sdk/google&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;embed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./supabaseClient&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Use public client for reads&lt;/span&gt;

&lt;span class="c1"&gt;// Match configuration with embedAndStore and DB schema&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EMBEDDING_MODEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;textEmbeddingModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-embedding-004&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RAW_VECTOR_DIMENSIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OPTIMIZED_VECTOR_DIMENSIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Must match embedAndStore if optimize=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Re-use Helper Functions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Include the same &lt;code&gt;normalizeL2&lt;/code&gt; and &lt;code&gt;optimizeEmbedding&lt;/code&gt; helper functions used during storage to ensure the query vector is processed identically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... imports and config from above&lt;/span&gt;

&lt;span class="c1"&gt;// --- Re-use Helper: Normalize L2 ---&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalizeL2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;norm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;norm&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// --- Re-use Helper: Optimize Embedding ---&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;optimizeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="nx"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// (Identical implementation as in embeddingUtils.ts)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dimension&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dimension&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dimension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;truncated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;dimension&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeL2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;truncated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Search query optimization resulted in unexpected dimension: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Core Search Function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This function takes a search query, generates its embedding, optimizes it (if the stored embeddings were optimized), and then calls the &lt;code&gt;match_documents&lt;/code&gt; Supabase RPC function to find similar documents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... imports, config, and helpers from above&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;semanticSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;optimize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// MUST match the optimization state used for storing&lt;/span&gt;
  &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Similarity threshold&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;similarity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Generate Query Embedding&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rawQueryEmbedding&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EMBEDDING_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// Optional: Use Google's built-in dimensionality reduction&lt;/span&gt;
      &lt;span class="c1"&gt;// ...(optimize ? { parameters: { outputDimensionality: OPTIMIZED_VECTOR_DIMENSIONS } } : {}),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Optimize Query Embedding (must match storage optimization strategy)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryEmbedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;optimize&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;optimizeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawQueryEmbedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OPTIMIZED_VECTOR_DIMENSIONS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;optimizeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawQueryEmbedding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Normalize only&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate query embedding length&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expectedDimension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;optimize&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;OPTIMIZED_VECTOR_DIMENSIONS&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RAW_VECTOR_DIMENSIONS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;expectedDimension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Query embedding dimension mismatch: expected &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;expectedDimension&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, got &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Call Supabase RPC function&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;match_documents&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;query_embedding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;match_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;match_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Supabase RPC error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Semantic search failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error in semanticSearch process:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* Example Usage Placeholder: Add example API route/Page call here if needed */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative Approach: Automatic Database-Driven Embeddings
&lt;/h2&gt;

&lt;p&gt;While the application-level approach works well for many use cases, we can also implement a more automated pattern where the database itself manages the embedding generation process.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Database-Driven Embeddings Work
&lt;/h3&gt;

&lt;p&gt;Instead of managing embeddings at the application level (as shown in the examples above), we can use PostgreSQL's trigger system to automatically generate and update embeddings when data changes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When a row is inserted or updated, a database trigger fires&lt;/li&gt;
&lt;li&gt;The trigger enqueues an embedding job via &lt;code&gt;pgmq&lt;/code&gt; (PostgreSQL Message Queue)&lt;/li&gt;
&lt;li&gt;A background worker (&lt;code&gt;pg_cron&lt;/code&gt;) processes the queue&lt;/li&gt;
&lt;li&gt;The worker calls an Edge Function via &lt;code&gt;pg_net&lt;/code&gt; to generate the embedding&lt;/li&gt;
&lt;li&gt;The embedding is stored back in the vector column&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This pattern has several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No embedding drift&lt;/strong&gt;: Your vectors automatically stay in sync with source content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fully asynchronous&lt;/strong&gt;: Write operations aren't slowed down by embedding generation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilient&lt;/strong&gt;: Jobs are retried if they fail, ensuring embedding consistency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL-native&lt;/strong&gt;: The entire process is managed within PostgreSQL&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation Example
&lt;/h3&gt;

&lt;p&gt;Here's a simplified example of how to set up database-driven automatic embeddings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create a table with a vector column&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="n"&gt;ALWAYS&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;-- For Google's text-embedding-004&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Create a function that specifies what content to embed&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;embedding_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;
&lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;
&lt;span class="k"&gt;IMMUTABLE&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="s1"&gt;'# '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Add triggers to handle inserts and updates&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;embed_documents_on_insert&lt;/span&gt;
  &lt;span class="k"&gt;AFTER&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;
  &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;EACH&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
  &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue_embeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'embedding_input'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'embedding'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;embed_documents_on_update&lt;/span&gt;
  &lt;span class="k"&gt;AFTER&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;OF&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;
  &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;EACH&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
  &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue_embeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'embedding_input'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'embedding'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach removes the need for the application-level embedding logic we implemented earlier. When you insert or update a document, the embedding is automatically generated and stored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Insert a document (embedding will be generated asynchronously)&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Understanding Embeddings'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Embeddings are vector representations of text...'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initially, the &lt;code&gt;embedding&lt;/code&gt; column will be null, but within a few seconds, it will be automatically populated by the background process.&lt;/p&gt;

&lt;p&gt;This pattern is particularly useful for production systems where you need to ensure embeddings always stay in sync with your content without building complex application logic.&lt;/p&gt;

&lt;p&gt;The implementation requires setting up Supabase Edge Functions to handle the actual embedding generation and PostgreSQL extensions like &lt;code&gt;pgmq&lt;/code&gt;, &lt;code&gt;pg_cron&lt;/code&gt;, and &lt;code&gt;pg_net&lt;/code&gt; to manage the asynchronous workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You've now learned how to generate Google Gemini embeddings using the Vercel AI SDK, optionally optimize them, store them efficiently in Supabase with &lt;code&gt;pgvector&lt;/code&gt;, and perform semantic searches. This powerful combination enables sophisticated AI features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Meaning-based Search:&lt;/strong&gt; Go beyond keywords to find truly relevant content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recommendation Systems:&lt;/strong&gt; Suggest similar items based on semantic understanding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAG (Retrieval-Augmented Generation):&lt;/strong&gt; Find relevant context to enhance LLM responses for Q&amp;amp;A bots.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Vercel AI SDK simplifies AI model interactions, while Supabase and &lt;code&gt;pgvector&lt;/code&gt; provide a robust, scalable backend. Remember to maintain consistency in embedding dimensions between storage and search, especially when using optimization techniques.&lt;/p&gt;

&lt;p&gt;Happy building!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>supabase</category>
      <category>gemini</category>
      <category>vercel</category>
    </item>
    <item>
      <title>GitHub Copilot — Agent Mode Review</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Sun, 09 Feb 2025 18:49:26 +0000</pubDate>
      <link>https://dev.to/danielsogl/github-copilot-agent-mode-review-4h23</link>
      <guid>https://dev.to/danielsogl/github-copilot-agent-mode-review-4h23</guid>
      <description>&lt;p&gt;The year 2025 is dominated by the new AI buzzword: AI agents. Tools like Cursor, Windsurf, and Cline have been offering so-called agent modes for quite some time, allowing developers to automate programming tasks. Surprisingly, GitHub Copilot, the most well-known AI-powered development tool, had not included such a feature — until now. With the introduction of Agent Mode, available in preview for Visual Studio Code, Copilot has taken a major step towards autonomous coding assistance.&lt;/p&gt;

&lt;p&gt;Over the past few days, I have tested this new feature extensively. In this review, I’ll provide an overview of what GitHub Copilot’s Agent Mode can and cannot do, as well as areas where GitHub (or Microsoft) still needs to improve.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is GitHub Copilot’s Agent Mode?
&lt;/h2&gt;

&lt;p&gt;An AI agent is an autonomous system designed to perform tasks, make decisions, and adapt based on data and user feedback. It operates independently, automating processes across various domains, including software development.&lt;/p&gt;

&lt;p&gt;Copilot’s Agent Mode follows this principle: it autonomously reads files, makes decisions, and executes code edits without requiring manual confirmation for each change. Unlike the traditional Copilot mode, users don’t have to approve every single modification. Instead, after the Agent completes a task, users can review and approve or reject the suggested changes.&lt;/p&gt;

&lt;p&gt;GitHub Copilot’s new Agent Mode is capable of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Iterating on its own code, recognizing errors, and fixing them automatically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Suggesting terminal commands and prompting users to execute them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Analyzing run-time errors and performing self-healing operations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;To use the Agent Mode, you need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Download and install the &lt;a href="https://code.visualstudio.com/insiders/" rel="noopener noreferrer"&gt;&lt;strong&gt;Insider version&lt;/strong&gt;&lt;/a&gt; of Visual Studio Code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub Copilot plugin&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;(Preview version).&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Link your &lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub account&lt;/strong&gt;&lt;/a&gt; to enable the feature.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open the &lt;strong&gt;Copilot Edit panel&lt;/strong&gt; and select &lt;strong&gt;Agent Mode&lt;/strong&gt; from the menu at the bottom.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9e1qwkx6hsxire2v24is.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9e1qwkx6hsxire2v24is.png" alt="Copilot Agent Image" width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once these steps are completed, you can start experimenting with Copilot’s new autonomous capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases for Copilot Agent
&lt;/h2&gt;

&lt;p&gt;While the standard Copilot Edit Mode is great for implementing small features and making incremental improvements, Agent Mode is designed to handle larger, more complex tasks. Since I primarily work in the Angular ecosystem, I tested Agent Mode with Angular projects, but the workflow remains the same across different frameworks and technologies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test #1: Building a TodoMVC App with Angular
&lt;/h2&gt;

&lt;p&gt;For my first test, I asked the Agent to create a &lt;strong&gt;TodoMVC application using Angular&lt;/strong&gt; based on a simple screenshot. Initially, I planned to use &lt;strong&gt;Sonnet-3.5&lt;/strong&gt;, but since Copilot does not yet support image inputs, I had to switch to &lt;strong&gt;GPT-4o&lt;/strong&gt;for this task.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbcnz08o09avop54rmem9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbcnz08o09avop54rmem9.png" alt="Todo MVC" width="800" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, the Agent struggled with this seemingly simple request.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It correctly initialized a new Angular project using the &lt;strong&gt;Angular CLI&lt;/strong&gt; and started generating components and styles.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;However, since &lt;strong&gt;GPT-4o was trained on Angular 15&lt;/strong&gt;, it could not use the latest Angular features in a brand-new project like the new control flow or signals.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Even worse, the Agent failed to reference files it had previously generated, making it difficult to maintain project context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Additionally, it was unable to &lt;strong&gt;resolve compile errors autonomously&lt;/strong&gt;, getting stuck in an infinite loop, focusing on irrelevant files.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Test #2: Enhancing an Existing Next.js App
&lt;/h2&gt;

&lt;p&gt;In my second test, I asked the Agent to &lt;strong&gt;add a Testimonial section to an existing Next.js application&lt;/strong&gt;, ensuring it was responsive. This time, I opted for &lt;strong&gt;Sonnet-3.5&lt;/strong&gt; instead of GPT-4o.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Agent correctly &lt;strong&gt;read the project’s package.json&lt;/strong&gt; to identify relevant dependencies (Next.js, Tailwind, and Radix UI).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It then &lt;strong&gt;generated the required components&lt;/strong&gt; and integrated them into the application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;Next.js config was modified&lt;/strong&gt; to allow the display of placeholder images.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The initial implementation looked decent. When I later asked the Agent to &lt;strong&gt;enable automatic scrolling&lt;/strong&gt; when the user reached the section, it also handled this well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unlike in the first test, the Agent successfully fixed &lt;strong&gt;compile-time errors autonomously&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvq0fyuadiw88foeo35pi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvq0fyuadiw88foeo35pi.png" alt="Testimonial Demo" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This test clearly demonstrated that Copilot’s Agent Mode works significantly better &lt;strong&gt;when given access to an existing codebase&lt;/strong&gt; rather than generating a project from scratch. This aligns with my experience testing other AI coding agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test #3: Code Review and Unit Testing in a Next.js Project
&lt;/h2&gt;

&lt;p&gt;For the third test, I asked the Agent to &lt;strong&gt;review the code&lt;/strong&gt; from the previous task and &lt;strong&gt;add unit tests&lt;/strong&gt; using Jest. My goal was to determine whether:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The Agent unnecessarily altered its own previously generated code (a common issue with AI-generated code reviews).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It could independently set up Jest, since the project had no existing test setup.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First, the Agent &lt;strong&gt;scanned the entire codebase&lt;/strong&gt; to confirm that Jest was not yet installed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It performed the &lt;strong&gt;code review&lt;/strong&gt; but &lt;strong&gt;did not suggest any changes&lt;/strong&gt; — which was a positive sign.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It then &lt;strong&gt;attempted to set up Jest&lt;/strong&gt; and create unit tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Interestingly, the Agent &lt;strong&gt;aimed for 100% test coverage&lt;/strong&gt; but encountered a major issue: &lt;strong&gt;it got stuck in an infinite loop&lt;/strong&gt;, failing to resolve JSX-related errors properly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instead of fixing the JSX errors directly, it tried adding unnecessary dependencies, breaking the Jest setup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After multiple retries, it eventually managed to resolve the issue.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3g1kwh1rgkfobo5oa9hb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3g1kwh1rgkfobo5oa9hb.png" alt="Context Demo" width="800" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Preliminary Verdict
&lt;/h2&gt;

&lt;p&gt;GitHub Copilot’s Agent Mode is a long-overdue step forward in AI-powered coding assistance. It enables developers to tackle larger tasks with simple prompts, reducing the need for manual intervention.&lt;/p&gt;

&lt;p&gt;However, at the time of writing, &lt;strong&gt;Agent Mode still lags behind competitors like Cline and Cursor in key areas&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limited LLM support&lt;/strong&gt;: Unlike its competitors, Copilot currently lacks a variety of model options.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context limitations&lt;/strong&gt;: It does not yet allow for extended context inputs, such as local documentation or web links.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lack of robustness&lt;/strong&gt;: The Agent still struggles with &lt;strong&gt;compile-time errors and complex project structures&lt;/strong&gt;, often getting stuck in loops.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That being said, GitHub has confirmed that &lt;strong&gt;Agent Mode will eventually be available in all Copilot-supported IDEs&lt;/strong&gt;, and they are actively seeking feedback to improve the experience.&lt;/p&gt;

&lt;p&gt;I will continue monitoring GitHub Copilot’s Agent Mode and will share updates as new features are released. Stay tuned for future tests and comparisons!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The AI-Enhanced Developer: Maximizing Efficiency with Coding Agents</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Fri, 07 Feb 2025 17:57:14 +0000</pubDate>
      <link>https://dev.to/danielsogl/the-ai-enhanced-developer-maximizing-efficiency-with-coding-agents-2lmf</link>
      <guid>https://dev.to/danielsogl/the-ai-enhanced-developer-maximizing-efficiency-with-coding-agents-2lmf</guid>
      <description>&lt;p&gt;Software development has evolved far beyond just writing code—it’s about efficiency, smart solutions, and focusing on what truly matters. AI-powered coding tools like GitHub Copilot, Cursor, bolt.new, and v0 are revolutionizing the way we build software. These tools automate repetitive tasks, accelerate development processes, and create space for more creative and strategic work. However, they come with their own challenges and limitations. This article explores how AI tools optimize workflows and make day-to-day development more productive without compromising on quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rise of AI in Software Development
&lt;/h2&gt;

&lt;p&gt;AI-powered coding assistants have gained significant traction in recent years, transforming the way developers write and manage code. Industry leaders predict that AI agents will play an increasingly prominent role in software development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sam Altman (CEO of OpenAI):&lt;/strong&gt; &lt;em&gt;“We believe that in 2025, we might see the first AI agents join the workforce.”&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jensen Huang (CEO of Nvidia):&lt;/strong&gt; &lt;em&gt;“AI agents are going to get deployed; I think this year we are going to see it take off.”&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mark Zuckerberg (CEO of Meta):&lt;/strong&gt; &lt;em&gt;“AI will write most software soon.”&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These statements highlight the rapid advancements in AI-powered development. But will AI replace developers entirely? The more practical question is how AI tools can be leveraged to boost productivity rather than replace human expertise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding AI Agents and Their Role
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;AI agent&lt;/strong&gt; is an autonomous system designed to perform tasks, make decisions, and adapt based on data and user feedback. These agents operate independently, automating processes in various fields—including software development. AI coding tools fall into this category, helping developers write, refactor, and optimize code with increased efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI-Powered Coding Tools: What’s Available?
&lt;/h2&gt;

&lt;p&gt;There is an overwhelming number of AI tools available today. For simplicity, they can be categorized into two main types:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;AI-Driven Prototyping Tools&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;These tools focus on rapid prototyping, allowing developers to transform ideas into working applications with minimal effort.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://v0.dev" rel="noopener noreferrer"&gt;v0 by Vercel&lt;/a&gt;&lt;/strong&gt; – Best for generating React components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://bolt.new" rel="noopener noreferrer"&gt;Bolt.new&lt;/a&gt;&lt;/strong&gt; – Developed by StackBlitz, supports full-stack applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://lovable.dev" rel="noopener noreferrer"&gt;Lovable.dev&lt;/a&gt;&lt;/strong&gt; – Integrates deeply with Supabase, making it ideal for creating full-stack applications quickly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools help developers validate ideas quickly but are not meant for production-ready code. &lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;AI Coding Assistants and IDE Enhancements&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For real-world development, coding assistants provide greater value. These tools assist with writing, refactoring, and debugging code directly within the developer's workflow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.cursor.com" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;&lt;/strong&gt; – An AI-powered IDE that indexes codebases and external documentation for better AI-assisted development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://cline.bot" rel="noopener noreferrer"&gt;Cline&lt;/a&gt;&lt;/strong&gt; – A Visual Studio Code plugin that offers a transparent cost model and supports various LLMs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt;&lt;/strong&gt; – One of the most popular AI coding assistants, with code completion and inline suggestions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Practical Use Cases of AI Coding Tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Automating Repetitive Coding Tasks&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;AI assistants can automate mundane tasks such as writing boilerplate code, setting up configurations, and generating common UI components. Developers can focus on solving complex problems instead of redoing the same tasks repeatedly.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Improving Code Quality and Consistency&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;By leveraging AI-powered tools, developers can ensure consistency in their coding styles. Tools like Cursor allow developers to index their internal documentation and enforce coding guidelines through structured prompts.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Accelerating Prototyping and MVP Development&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Prototyping tools like v0, Bolt, and Lovable enable developers to quickly generate UI components and full-stack applications, reducing the time required to validate ideas and present functional prototypes.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Enhancing Debugging and Code Reviews&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;AI coding tools assist in debugging by analyzing logs, suggesting fixes, and automatically detecting patterns that may cause errors. They also facilitate code reviews by identifying areas for improvement based on predefined rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges and Limitations
&lt;/h2&gt;

&lt;p&gt;While AI tools are impressive, they are not without their downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cost Transparency:&lt;/strong&gt; Some tools (like Copilot) offer a flat rate, while others (like Cursor) charge based on API usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Quality Variability:&lt;/strong&gt; Without guidelines, AI-generated code can be inconsistent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM Model Latency:&lt;/strong&gt; Some AI models take time to process requests, which can impact efficiency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vendor Lock-in:&lt;/strong&gt; Tools like v0 may lock developers into specific ecosystems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lack of Angular-Specific Support:&lt;/strong&gt; Many AI coding assistants prioritize React, leaving Angular developers with fewer tailored solutions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best Practices for AI-Assisted Development
&lt;/h2&gt;

&lt;p&gt;To get the most out of AI coding tools, developers should consider the following workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define Your Goal &amp;amp; Tech Stack&lt;/strong&gt; – Use AI to refine ideas and outline project structures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prototype with AI-Driven Tools&lt;/strong&gt; – Utilize tools like v0, Bolt, or Lovable to create rapid prototypes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Develop in an AI-Assisted IDE&lt;/strong&gt; – Use Cursor, Cline, or Copilot for real-world coding tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan Before Execution&lt;/strong&gt; – Leverage AI’s planning features to avoid unnecessary refactoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ensure Code Quality &amp;amp; Compliance&lt;/strong&gt; – Use rule files to enforce coding standards and maintain consistency.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;AI coding tools are powerful allies in software development. They can boost productivity, automate repetitive tasks, and provide insightful recommendations. However, they should be used strategically, with human oversight, to ensure quality and maintainability. By integrating AI tools into the development workflow, developers can work smarter—not harder.&lt;/p&gt;

&lt;p&gt;For a deeper dive into these concepts, check out the recording of my talk linked below. Let me know your thoughts and experiences with AI coding tools in the comments! 🚀&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/DGzx1fToeBQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>ai</category>
      <category>coding</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Boosting Your Angular Development Workflow with Cursor Code Editor</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Sun, 05 Jan 2025 23:00:00 +0000</pubDate>
      <link>https://dev.to/danielsogl/boosting-your-angular-development-workflow-with-cursor-code-editor-37cb</link>
      <guid>https://dev.to/danielsogl/boosting-your-angular-development-workflow-with-cursor-code-editor-37cb</guid>
      <description>&lt;p&gt;The software development landscape is rapidly evolving, with AI-powered tools becoming integral to modern workflows. In recent years, we’ve seen the rise of coding assistants like GitHub Copilot, showcasing the transformative potential of Large Language Models (LLMs) and Generative AI (GenAI) in software development.&lt;/p&gt;

&lt;p&gt;Taking this innovation a step further is &lt;strong&gt;Cursor&lt;/strong&gt;, a Visual Studio Code fork that embeds generative AI features directly into its interface. Equipped with built-in chat, coding composer, coding agent, and advanced documentation indexing, Cursor is redefining how developers approach coding tasks.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore Cursor’s unique features and show how Angular developers can harness its capabilities to boost productivity and improve code quality.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What Is Cursor?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cursor is a next-generation, AI-powered code editor that builds on the familiar experience of Visual Studio Code (VS Code) while introducing advanced AI capabilities. Whether you’re looking to automate repetitive tasks, improve code quality, or gain deeper insights, Cursor offers a comprehensive solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features of Cursor&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tab Autocomplete&lt;/strong&gt;: Cursor predicts your next edits across multiple lines, intelligently adapting to your coding patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chat Integration&lt;/strong&gt;: Engage in natural language conversations with an AI that understands your codebase. The AI can analyze specific blocks of code or the entire project to provide suggestions, detect bugs, and even rewrite snippets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Rewrites&lt;/strong&gt;: Cursor proactively fixes syntax issues and optimizes code structure, ensuring clean and efficient output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Line Edits&lt;/strong&gt;: Suggests batch changes across your codebase to save time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal Models&lt;/strong&gt;: Supports multiple AI models like &lt;strong&gt;Claude 3.5 Sonnet&lt;/strong&gt;, &lt;strong&gt;o1&lt;/strong&gt;, and Cursor’s own &lt;strong&gt;cursor-fast&lt;/strong&gt; and &lt;strong&gt;cursor-mini&lt;/strong&gt;, which can also process images as reference points.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project Indexing:&lt;/strong&gt; Cursor indexes your project files to access your code base as fast as possible. This reduces the time to interact with your code dramatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation Indexing&lt;/strong&gt;: Cursor includes the largest framework documentations by default but developers can add any other documentation hosted in the web to index them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search the Web:&lt;/strong&gt; Fetches relevant information from the internet to enhance code suggestions and task handling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Migration from VS Code&lt;/strong&gt;: Migrating from VS Code is straightforward, ensuring developers can retain their existing workflows while upgrading to an AI-enhanced environment. (&lt;a href="https://docs.cursor.com/get-started/migrate-from-vscode" rel="noopener noreferrer"&gt;Learn how to migrate&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Using Cursor for Angular Development&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Angular developers can unlock even more potential with Cursor by leveraging its customization and documentation features. Here’s how:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Define Angular-Specific Coding Rules&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cursor allows developers to define custom coding rules, tailoring the editor to meet project-specific or team-wide standards. These rules can enforce Angular-specific conventions, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structuring components and services.&lt;/li&gt;
&lt;li&gt;Using reactive programming best practices with RxJS.&lt;/li&gt;
&lt;li&gt;Enforcing state management patterns with NgRx.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You only have to create a &lt;code&gt;.cursorrules&lt;/code&gt; file in the root of your project and past your custom rules into that file. Cursor will always take a look into that file when you write a prompt. &lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://cursor.directory/" rel="noopener noreferrer"&gt;Cursor Directory&lt;/a&gt; for a curated collection of reusable Cursor Rules. Developers can copy and paste these rules directly into their projects, saving time and ensuring consistency across the codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Supercharge Your Workflow with Documentation Indexing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The documentation indexing feature is particularly useful for Angular applications, where working with multiple interconnected modules, services, and components is common. By indexing your project and its libraries, Cursor enables developers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query library-specific documentation without leaving the editor.&lt;/li&gt;
&lt;li&gt;Resolve coding challenges faster by fetching method definitions and usage examples directly.&lt;/li&gt;
&lt;li&gt;Stay productive by keeping the most relevant information a keystroke away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbx5uwevu37m3vg53atc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbx5uwevu37m3vg53atc.png" alt="Add documentations to the cursor index" width="800" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, if you’re unsure how to set up an NgRx Effect, you can simply type your question into Cursor’s chat, and it will provide accurate guidance, often accompanied by example code. You can also include multiple documentations into your prompts. Just type &lt;code&gt;@Docs&lt;/code&gt; into the prompt field and select your documentation Cursor should use to resolve your task.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmd7ta7bsn9vwuzxyfhod.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmd7ta7bsn9vwuzxyfhod.png" alt="Use the documentation in your prompts" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Why Cursor Is a Game-Changer for Angular Developers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cursor stands out as an indispensable tool for Angular developers aiming to stay ahead of the curve. Its blend of AI-driven assistance, Angular-specific customization, and robust documentation integration simplifies the development process, reduces errors, and boosts productivity.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What’s Next?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Are you intrigued by Cursor’s potential? Would you like to see a detailed tutorial on setting up and customizing Cursor for Angular development? Share your thoughts in the comments below or reach out on social media. If there’s enough interest, I’ll dive deeper into Cursor’s features and show how you can maximize its capabilities for your Angular projects.&lt;/p&gt;

&lt;p&gt;Ready to try it yourself? &lt;a href="https://docs.cursor.com/get-started/migrate-from-vscode" rel="noopener noreferrer"&gt;Explore the official Cursor documentation&lt;/a&gt; and start building smarter today!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>ai</category>
      <category>coding</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Enhancing Side Effects in Angular with NgRx's signalMethod</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Mon, 30 Dec 2024 22:00:00 +0000</pubDate>
      <link>https://dev.to/danielsogl/enhancing-side-effects-in-angular-with-ngrxs-signalmethod-ho9</link>
      <guid>https://dev.to/danielsogl/enhancing-side-effects-in-angular-with-ngrxs-signalmethod-ho9</guid>
      <description>&lt;p&gt;NgRx has always been a trusted library for managing state in Angular applications. With the introduction of its Signals API, developers now have a more streamlined way to handle state changes and side effects. To handle those side effects the NgRx team recently introduced the &lt;code&gt;signalMethod&lt;/code&gt; that simplifies reactive workflows and makes them more intuitive.&lt;/p&gt;

&lt;p&gt;In this article, I’ll dive into the &lt;code&gt;signalMethod&lt;/code&gt;, showcasing how it works through practical examples and why it’s an excellent addition to your Angular toolkit.&lt;/p&gt;




&lt;h3&gt;
  
  
  What is &lt;code&gt;signalMethod&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;signalMethod&lt;/code&gt; is a utility introduced by NgRx that allows you to manage side effects in a clean, reactive way. Unlike traditional approaches that may rely on RxJS operators or manual effect management, &lt;code&gt;signalMethod&lt;/code&gt; lets you focus on what matters: the logic for handling changes.&lt;/p&gt;

&lt;p&gt;It achieves this by combining signals with a processor function. This function reacts to changes in its input, whether it's a static value or a reactive signal. The result is a flexible yet powerful way to handle state-driven actions.&lt;/p&gt;




&lt;h3&gt;
  
  
  Setting Up a Signal-Driven Action
&lt;/h3&gt;

&lt;p&gt;Let’s start with a simple example: logging a message whenever a number doubles. Here’s how you can achieve this using &lt;code&gt;signalMethod&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signalMethod&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ngrx/signals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-math-logger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;button (click)="increment()"&amp;gt;Increment&amp;lt;/button&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MathLoggerComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Define the signal method&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;logDoubledValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signalMethod&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doubled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Doubled Value: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;doubled&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logDoubledValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;logDoubledValue&lt;/code&gt; is invoked whenever the &lt;code&gt;increment&lt;/code&gt; method is called. The counter’s value is passed into the &lt;code&gt;signalMethod&lt;/code&gt;, which then logs the doubled value. This example shows how easily &lt;code&gt;signalMethod&lt;/code&gt; integrates with signals to react to state changes.&lt;/p&gt;




&lt;h3&gt;
  
  
  Reacting to Signals Dynamically
&lt;/h3&gt;

&lt;p&gt;Suppose you’re working on a temperature monitoring system. Instead of logging doubled values, you need to alert the user if the temperature exceeds a threshold. With &lt;code&gt;signalMethod&lt;/code&gt;, this becomes straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-temperature-monitor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;button (click)="increaseTemperature()"&amp;gt;Increase Temperature&amp;lt;/button&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TemperatureMonitorComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;temperature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Start at 20°C&lt;/span&gt;

  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;alertHighTemperature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signalMethod&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Warning: High temperature detected!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alertHighTemperature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;increaseTemperature&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time the temperature increases, the &lt;code&gt;alertHighTemperature&lt;/code&gt; method evaluates the new value and issues a warning if it’s too high.&lt;/p&gt;




&lt;h3&gt;
  
  
  Controlling Cleanup for Services
&lt;/h3&gt;

&lt;p&gt;In some cases, you might define &lt;code&gt;signalMethod&lt;/code&gt; in a service rather than directly in a component. This allows for better reuse but requires careful management of side effects to avoid memory leaks.&lt;/p&gt;

&lt;p&gt;Here’s an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signalMethod&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ngrx/signals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ScoreService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;logScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signalMethod&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Current Score: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, suppose a component uses this service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-score-tracker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;button (click)="updateScore()"&amp;gt;Update Score&amp;lt;/button&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ScoreTrackerComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;scoreService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ScoreService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scoreService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logScore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;updateScore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To prevent memory leaks, make sure to inject the component’s context when using the service in dynamic scenarios:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ScoreTrackerComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;injector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Injector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;scoreService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ScoreService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scoreService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logScore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;injector&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;updateScore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Why Choose &lt;code&gt;signalMethod&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;Compared to other tools like &lt;code&gt;effect&lt;/code&gt;, &lt;code&gt;signalMethod&lt;/code&gt; offers some distinct advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: It can process both static values and reactive signals, making it ideal for mixed scenarios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Dependency Tracking&lt;/strong&gt;: Tracks only the provided signal, reducing the risk of unintended dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Context Management&lt;/strong&gt;: Works seamlessly in Angular injection contexts and allows manual control when needed.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  A Note on When to Use &lt;code&gt;signalMethod&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;signalMethod&lt;/code&gt; is perfect for handling straightforward side effects in reactive workflows, there are scenarios where RxJS might be a better fit—especially for complex streams or cases involving race conditions. However, for applications leveraging NgRx signals, &lt;code&gt;signalMethod&lt;/code&gt; strikes a perfect balance of simplicity and power.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;NgRx’s &lt;code&gt;signalMethod&lt;/code&gt; is a game-changer for handling side effects in Angular applications. By combining the elegance of signals with the flexibility of processor functions, it simplifies reactive patterns while maintaining control over cleanup and dependencies.&lt;/p&gt;

&lt;p&gt;If you’re using NgRx Signals in your projects, I highly recommend exploring &lt;code&gt;signalMethod&lt;/code&gt; to streamline your side-effect handling. Try it out and experience how it can make your Angular applications cleaner and more reactive!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Flexible Angular Builds: A Guide to Angular 19 Build-Time Variables with Docker</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Tue, 10 Dec 2024 23:00:00 +0000</pubDate>
      <link>https://dev.to/danielsogl/flexible-angular-builds-a-guide-to-angular-19-build-time-variables-with-docker-3ld1</link>
      <guid>https://dev.to/danielsogl/flexible-angular-builds-a-guide-to-angular-19-build-time-variables-with-docker-3ld1</guid>
      <description>&lt;p&gt;When deploying Angular applications as containers, managing environment variables at build time has always been a challenge. With Angular 19, the CLI now supports passing variables directly at build time. In this article, I’ll demonstrate how to dockerize your Angular application and leverage build-time variables for seamless containerized deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Are Build-Time Variables?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Build-time variables allow you to inject configuration values during the build process. These variables are embedded into the application, enabling different behaviors or settings based on the build environment.&lt;/p&gt;

&lt;p&gt;This approach is perfect for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Environment-Specific API Endpoints:&lt;/strong&gt; Switch between development, staging, and production configurations seamlessly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature Flags:&lt;/strong&gt; Enable or disable features dynamically at build time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure Secrets Management:&lt;/strong&gt; Avoid exposing sensitive information in your source code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide: Dockerizing an Angular App with Build-Time Variables
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Define Variables in the Environment File&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Start by defining your variables as constants in an &lt;code&gt;environment.ts&lt;/code&gt; file (or another dedicated file). You can optionally define default values and export them as an &lt;code&gt;environment&lt;/code&gt; object for reuse in your Angular project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Create a Dockerfile with Build Arguments&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Use a Dockerfile to define how the build-time variables will be injected. Below is a basic example; for a more complex setup, check the linked GitHub repository at the end of the article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stage 1: Build Angular app&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:lts-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;

&lt;span class="c"&gt;# Set working directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy package files and install dependencies&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Copy source code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Define build arguments and use them during the build&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; API_KEY&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; CONFIGURATION=production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; API_KEY=$API_KEY&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; CONFIGURATION=$CONFIGURATION&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--configuration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$CONFIGURATION&lt;/span&gt; &lt;span class="nt"&gt;--define&lt;/span&gt; &lt;span class="nv"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt; &lt;span class="nt"&gt;--define&lt;/span&gt; &lt;span class="nv"&gt;configuration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$CONFIGURATION&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;

&lt;span class="c"&gt;# Stage 2: Serve the app using Nginx&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx.conf /etc/nginx/nginx.conf&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/dist /usr/share/nginx/html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new &lt;code&gt;--define&lt;/code&gt; option passes the variable to the bundler and replaces the placeholder values we created in the first step.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Pass Build-Time Variables in Docker Compose&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Create a docker-compose.yml file to pass values for the build arguments defined in the Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;webapp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;API_KEY=YOUR_PRODUCTION_API_KEY&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CONFIGURATION=production&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4200:80"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;NODE_ENV=production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup ensures that YOUR_PRODUCTION_API_KEY and production are injected into your Angular build process during container creation.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 4: Access Build-Time Variables in Your Application&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In your Angular application, use the injected variables like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div class="container"&amp;gt;
      &amp;lt;h1&amp;gt;Angular Environment Variables Demo&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;Configuration: {{ configuration }}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;API Key: {{ apiKey }}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;configuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 5: Configure Nginx for Production&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, configure Nginx to serve your Angular application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Place this configuration file (nginx.conf) in your project directory, and it will be included in the Docker image during the build process.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Benefits of Combining Docker and Build-Time Variables&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;• &lt;strong&gt;Security:&lt;/strong&gt; Embed secrets like API keys during the build process, avoiding exposure in source code or runtime environments.&lt;/p&gt;

&lt;p&gt;• &lt;strong&gt;Environment Flexibility:&lt;/strong&gt; Switch environments by modifying Docker Compose files without hardcoding changes.&lt;/p&gt;

&lt;p&gt;• &lt;strong&gt;Streamlined CI/CD:&lt;/strong&gt; Automate builds for multiple environments, enhancing deployment efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;With Angular 19’s build-time variables and Docker, you can streamline application builds, manage environment-specific configurations effortlessly, and enhance security. This combination is essential for developers looking to create scalable and flexible deployment pipelines. As promised I created a GitHub repository with a extended Docker file that can be found here: &lt;a href="https://github.com/danielsogl/ng-env-docker-demo" rel="noopener noreferrer"&gt;https://github.com/danielsogl/ng-env-docker-demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try Dockerizing your Angular application today and share your experiences!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>docker</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>Functional Programming in Angular: Exploring inject and Resources</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Wed, 04 Dec 2024 19:41:52 +0000</pubDate>
      <link>https://dev.to/danielsogl/functional-programming-in-angular-exploring-inject-and-resources-d4b</link>
      <guid>https://dev.to/danielsogl/functional-programming-in-angular-exploring-inject-and-resources-d4b</guid>
      <description>&lt;p&gt;Angular’s evolving ecosystem is shifting toward a more &lt;strong&gt;functional and reactive programming&lt;/strong&gt; paradigm. With tools like &lt;strong&gt;Signals&lt;/strong&gt;, the &lt;strong&gt;Resource API&lt;/strong&gt;, and the &lt;strong&gt;&lt;code&gt;inject&lt;/code&gt; function&lt;/strong&gt;, developers can simplify application logic, reduce boilerplate, and enhance reusability.&lt;/p&gt;

&lt;p&gt;This blog post explores how Angular’s modern features empower developers to handle asynchronous logic in a clean, declarative, and reactive way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Benefits of Angular’s Functional Features
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reusable Functions with Dependency Injection&lt;/strong&gt;: The &lt;code&gt;inject&lt;/code&gt; function allows developers to create standalone functions that seamlessly integrate with Angular's dependency injection system. This decouples business logic from components and services, making functions reusable across the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified State Management&lt;/strong&gt;: Automatically handle loading, success, and error states.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Reactivity&lt;/strong&gt;: Automatically update data when dependencies change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Boilerplate&lt;/strong&gt;: Focus on the logic, not manual subscriptions or lifecycle management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Readability&lt;/strong&gt;: Declarative templates make UI state transitions easy to understand.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1: The API and Data Model
&lt;/h2&gt;

&lt;p&gt;For this example, we’ll fetch posts from a REST API. Each post has the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The base URL for the API is provided via an &lt;code&gt;InjectionToken&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;InjectionToken&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;InjectionToken&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Define Data Fetching Functions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Traditional RxJS-Based Approach
&lt;/h3&gt;

&lt;p&gt;The following function fetches a post by its ID using Angular’s &lt;code&gt;HttpClient&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../tokens/base-url.token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./post.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPostById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/posts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use this function in a component, you can bind it to an observable and display the result with the &lt;code&gt;async&lt;/code&gt; pipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AsyncPipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JsonPipe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getPostById&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./shared/posts.inject&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AsyncPipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JsonPipe&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    @if (post$ | async; as post) {
      &amp;lt;p&amp;gt;{{ post | json }}&amp;lt;/p&amp;gt;
    } @else {
      &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;
    }
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;postId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;post$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPostById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Limitations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reactivity Issues&lt;/strong&gt;: Signal changes (e.g., &lt;code&gt;postId&lt;/code&gt;) don’t automatically trigger a new fetch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual Error Handling&lt;/strong&gt;: You must write custom logic for loading and error states.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Signal-Based Resource API Approach
&lt;/h3&gt;

&lt;p&gt;The Resource API simplifies reactivity and state management. Here’s a function that uses the Resource API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ResourceRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../tokens/base-url.token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPostByIdResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ResourceRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;abortSignal&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/posts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;abortSignal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically reloads data when &lt;code&gt;postId&lt;/code&gt; changes.&lt;/li&gt;
&lt;li&gt;Handles &lt;code&gt;loading&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, and &lt;code&gt;success&lt;/code&gt; states declaratively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;JsonPipe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getPostByIdResource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./shared/posts.inject&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;JsonPipe&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    @if (post.isLoading()) {
      &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;
    } @else if (post.error()) {
      &amp;lt;p&amp;gt;Error: {{ post.error() }}&amp;lt;/p&amp;gt;
    } @else {
      &amp;lt;p&amp;gt;{{ post.value() | json }}&amp;lt;/p&amp;gt;
    }
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;postId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPostByIdResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Features of the Resource API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Declarative State Management
&lt;/h3&gt;

&lt;p&gt;The Resource API automatically manages states like &lt;code&gt;loading&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, and &lt;code&gt;success&lt;/code&gt;. This removes the need for custom flags and ensures cleaner templates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reactivity
&lt;/h3&gt;

&lt;p&gt;The Resource API is tightly integrated with Signals. Changes to a Signal automatically trigger the loader function, ensuring that your UI always reflects the latest data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error Handling
&lt;/h3&gt;

&lt;p&gt;Errors are centralized and exposed via &lt;code&gt;.error()&lt;/code&gt;, simplifying error management in templates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatic Lifecycle Management
&lt;/h3&gt;

&lt;p&gt;The API cancels ongoing requests when dependencies (e.g., &lt;code&gt;postId&lt;/code&gt;) change, preventing race conditions and stale data.&lt;/p&gt;




&lt;h2&gt;
  
  
  RxJS vs Resource API: A Quick Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;RxJS (Observable)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Resource API (Signal)&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Automatic (&lt;code&gt;loading&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reactivity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires custom setup&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Declarative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lifecycle Handling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires cleanup&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Angular’s &lt;code&gt;inject&lt;/code&gt; function and Signal-based Resource API represent a leap forward in simplifying asynchronous logic. With these tools, developers can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Decouple business logic from components.&lt;/li&gt;
&lt;li&gt;Write reusable functions that integrate seamlessly with Angular’s dependency injection system.&lt;/li&gt;
&lt;li&gt;Eliminate boilerplate for state management.&lt;/li&gt;
&lt;li&gt;Build reactive and declarative applications with ease.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Resource API, in particular, is ideal for modern Angular projects, providing automatic reactivity and declarative state handling. Start exploring these features today and take your Angular development to the next level!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>From Flaky to Flawless: Angular API Response Management with Zod</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Thu, 25 Apr 2024 14:43:45 +0000</pubDate>
      <link>https://dev.to/danielsogl/from-flaky-to-flawless-angular-api-response-management-with-zod-4013</link>
      <guid>https://dev.to/danielsogl/from-flaky-to-flawless-angular-api-response-management-with-zod-4013</guid>
      <description>&lt;p&gt;As a front-end developer, this has surely happened to you before. You start your computer in the morning, launch your Angular project, open &lt;a href="http://localhost:4200/" rel="noopener noreferrer"&gt;http://localhost:4200&lt;/a&gt; and are greeted by several runtime errors that weren't there the previous day. Your date pipe returns an 'Invalid Date' string and some components don't even render anymore. After your unit tests are still green and you haven't changed any other code since the last workday, there's only one culprit left: the backend. To save you work in the future and find faulty backend responses that were not agreed with you, I will show you a simple, but powerful solution with zod in this blog post.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Zod 101: An Introduction to Schema Validation in TypeScript&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://zod.dev/" rel="noopener noreferrer"&gt;Zod&lt;/a&gt; is an open-source schema declaration and validation library that emphasizes TypeScript. It can refer to any data type, from simple to complex. Zod eliminates duplicative type declarations by inferring static TypeScript types and allows easy composition of complex data structures from simpler ones. It has no dependencies, is compatible with Node.js and modern browsers, and has a concise, chainable interface. Zod is lightweight (8kb when zipped), immutable, with methods returning new instances. It encourages parsing over validation and is not limited to TypeScript but works well with JavaScript as well.&lt;/p&gt;

&lt;p&gt;Let's examine a simple example of how Zod helps us define and validate schemas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// creating a schema for strings&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mySchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// parsing&lt;/span&gt;
&lt;span class="nx"&gt;mySchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tuna&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; "tuna"&lt;/span&gt;
&lt;span class="nx"&gt;mySchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; throws ZodError&lt;/span&gt;

&lt;span class="c1"&gt;// "safe" parsing (doesn't throw error if validation fails)&lt;/span&gt;
&lt;span class="nx"&gt;mySchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tuna&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; { success: true; data: "tuna" }&lt;/span&gt;
&lt;span class="nx"&gt;mySchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; { success: false; error: ZodError }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using the safeParse function, we can return responses from the backend to our components while displaying any validation errors.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Defining API Response Models with Zod&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Now, let's examine a schema that defines the response model from the backend. For this example, I'll use the &lt;a href="https://jsonplaceholder.typicode.com/" rel="noopener noreferrer"&gt;JSONPlaceholder API&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CommentSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Comment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;CommentSchema&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As illustrated, I've defined the Zod schema as an exported constant, which can be used later in a function that's yet to be defined. Additionally, I've exported a type using the infer function, allowing it to be used in your Angular code without referencing the Zod library.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;How to Validate API Responses Using Zod in Angular&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;After creating our zod schema and exporting the type based on that schema, we can construct a helper function to validate our API responses. Given that the Angular HttpClient returns Observables, it's logical to create a custom rxjs operator function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isDevMode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pipe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verifyResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ZodTypeAny&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;zodObj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isDevMode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;zodObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;verifyResponse&lt;/code&gt; function takes a Zod object as an argument. It uses the &lt;code&gt;pipe&lt;/code&gt; function from RxJS to create a new function that maps responses to a validation process.&lt;/p&gt;

&lt;p&gt;If the Angular application is in development mode (&lt;code&gt;isDevMode()&lt;/code&gt;), it attempts to parse the response using Zod's &lt;code&gt;safeParse&lt;/code&gt; method. If parsing is unsuccessful, it logs the error to the console. Although you could use the parse function to validate the response model, this would throw an error that needs to be caught. To make identifying incorrect response models simpler, I recommend using the error handling as demonstrated.&lt;/p&gt;

&lt;p&gt;Finally, regardless of whether it's in development mode or not, it returns the unmodified response. This allows any components or services using this helper function to behave as if it wasn't there when not in development mode.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Integrating Schema Definitions and Validation in Angular Services&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;We can now bring everything together and define our Angular service. This service will call our backend and validate our response model. Thanks to Zod, the response is automatically typed based on the given schema. However, it's always good practice to explicitly define the return type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CommentSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Comment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../models/comment.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;verifyResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../utils/schema.validator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CommentsService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;getCommentById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;('https://jsonplaceholder.typicode.com/comments/' + id)
      .pipe(verifyResponse(CommentSchema));
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To illustrate the error output, I modified the schema to assume incorrect property types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;schema.validator.ts:12 ZodError: &lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"code"&lt;/span&gt;: &lt;span class="s2"&gt;"invalid_type"&lt;/span&gt;,
    &lt;span class="s2"&gt;"expected"&lt;/span&gt;: &lt;span class="s2"&gt;"number"&lt;/span&gt;,
    &lt;span class="s2"&gt;"received"&lt;/span&gt;: &lt;span class="s2"&gt;"string"&lt;/span&gt;,
    &lt;span class="s2"&gt;"path"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"body"&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="s2"&gt;"message"&lt;/span&gt;: &lt;span class="s2"&gt;"Expected number, received string"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In conclusion, utilizing Zod for API response validation can significantly improve the robustness of your Angular application. It allows you to catch and handle unexpected or incorrect backend responses before they cause runtime errors in your application. By integrating Zod with Angular, you can ensure that your frontend codebase is more resilient and less prone to bugs due to faulty backend responses. Remember, it's always better to prevent errors than to debug them!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Streamlining Coverage Reports in SonarCloud with an NX Monorepo</title>
      <dc:creator>Daniel Sogl</dc:creator>
      <pubDate>Tue, 16 Apr 2024 16:04:20 +0000</pubDate>
      <link>https://dev.to/danielsogl/streamlining-coverage-reports-in-sonarcloud-with-an-nx-monorepo-26n6</link>
      <guid>https://dev.to/danielsogl/streamlining-coverage-reports-in-sonarcloud-with-an-nx-monorepo-26n6</guid>
      <description>&lt;p&gt;After setting up your nx-workspace project, including your apps and libraries, you also want to use the power of nx-affected commands and the nx cloud. Perhaps you, like me, want also to define multiple code quality gates, including coverage reports for new and existing code in your repository. To handle that goal, I’m using SonarCube or SonarCloud for all of my professional projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmlpak6rv43o7pvw8ygu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmlpak6rv43o7pvw8ygu.png" alt="Sonar Scan Example" width="800" height="831"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sonarsource.com/products/sonarcloud/" rel="noopener noreferrer"&gt;SonarCloud&lt;/a&gt; makes it possible to auto-scan your repositories without setting up a CI pipeline. All you have to do is create a free SonarCloud account, connect your GitHub project, and commit and push the sonar config file. But with this approach, it is impossible to generate a coverage report for your repository. To handle this issue, Sonar provides a &lt;a href="https://github.com/SonarSource/sonarcloud-github-action" rel="noopener noreferrer"&gt;GitHub action&lt;/a&gt; to upload your generated coverage report as part of your defined quality gates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj58z93dm7h1nhgypzbjo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj58z93dm7h1nhgypzbjo.png" alt="nx project screenshot" width="800" height="666"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Necessary Steps to Enhance Code Quality Reporting
&lt;/h1&gt;

&lt;p&gt;To solve this problem, the following steps are necessary:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;run the test command on all nx-projects without using affected&lt;/li&gt;
&lt;li&gt;add a custom script to merge coverage reports into one file&lt;/li&gt;
&lt;li&gt;create a sonar-project.properties file and define the coverage report path&lt;/li&gt;
&lt;li&gt;upload the coverage report to SonarCloud using the official Sonar GitHub action.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1: Running Tests Across All Projects
&lt;/h2&gt;

&lt;p&gt;The first step is kind of simple. nx provides the run-many command to run a specified target on all nx-projects. In the past, I also had the best experience with Sonar using lcov reports.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nx run-many &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--parallel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="nt"&gt;--ci&lt;/span&gt; &lt;span class="nt"&gt;--code-coverage&lt;/span&gt; &lt;span class="nt"&gt;--coverageReporters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;lcov
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 2: Merging Coverage Reports
&lt;/h2&gt;

&lt;p&gt;The second step is also not that complicated. I wrote a simple JavaScript function to loop through the coverage folder and merge the project lcov files into one single file. The script is placed inside the tools/scripts folder.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;glob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;glob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getLcovFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/**/lcov.info`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
      &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getLcovFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;coverage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mergedReport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;mergedReport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mergedReport&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currFile&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./coverage/lcov.info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;mergedReport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The file has been saved!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 3 &amp;amp; 4: Configuring and Uploading the Coverage Report
&lt;/h2&gt;

&lt;p&gt;I added the merge command to my nx-cloud GitHubAction file. This will add the executed command to the nx-cloud report posted by nx in all pull requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr6a2uq7vv6ho50lfzzza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr6a2uq7vv6ho50lfzzza.png" alt="report example" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also added the coverage report as an artifact to use it later with the Sonar GitHub action. You can take a look at my configuration here.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Based on your Sonar configuration, your config file will differ from mine. The demo project’s related config file looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;sonar.projectKey=danielsogl_nx-sonar-example
sonar.organization=danielsogl
sonar.host.url=https://sonarcloud.io

&lt;span class="gh"&gt;# This is the name and version displayed in the SonarCloud UI.&lt;/span&gt;
&lt;span class="gh"&gt;#sonar.projectName=nx-sonar-example&lt;/span&gt;
&lt;span class="gh"&gt;#sonar.projectVersion=1.0&lt;/span&gt;

&lt;span class="gh"&gt;# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.&lt;/span&gt;
&lt;span class="gh"&gt;#sonar.sources=.&lt;/span&gt;

&lt;span class="gh"&gt;# Encoding of the source code. Default is default system encoding&lt;/span&gt;
sonar.sourceEncoding=UTF-8

sonar.test.inclusions=&lt;span class="ge"&gt;**&lt;/span&gt;/&lt;span class="err"&gt;*&lt;/span&gt;.spec.ts
sonar.typescript.lcov.reportPaths=coverage/lcov.info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After all CI steps are executed, the Sonar action will download the previously created coverage report artifact and upload it.&lt;/p&gt;

&lt;p&gt;When I now create a &lt;a href="https://github.com/danielsogl/nx-sonar-example/pull/2" rel="noopener noreferrer"&gt;new pull request&lt;/a&gt;, Sonar will not only check my code quality gates. Sonar now also checks my new checked-in code and its coverage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjgpwbj9rd4ey5f0au36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjgpwbj9rd4ey5f0au36.png" alt="scan result" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I created a public demo repository where you can take a closer look at my solution: &lt;a href="https://github.com/danielsogl/nx-sonar-example" rel="noopener noreferrer"&gt;https://github.com/danielsogl/nx-sonar-example&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>devops</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
