[{"content":"Introduction Angular 18 introduced a powerful reactivity model with Signals — and combined with the Claude API, it unlocks a new class of AI-first web applications. In this post, we\u0026rsquo;ll walk through building a reactive AI assistant component from scratch.\nWhy Signals for AI Apps? Traditional Angular apps used RxJS observables for async state. While powerful, they add cognitive overhead when dealing with streaming AI responses. Signals simplify this:\n// Traditional RxJS approach aiResponse$: Observable\u0026lt;string\u0026gt;; // Signals approach — cleaner, more intuitive aiResponse = signal\u0026lt;string\u0026gt;(\u0026#39;\u0026#39;); isLoading = signal\u0026lt;boolean\u0026gt;(false); Setting Up Claude API in Angular Install the HTTP client and set up your environment:\n// environment.ts export const environment = { claudeApiUrl: \u0026#39;https://api.anthropic.com/v1/messages\u0026#39;, claudeModel: \u0026#39;claude-sonnet-4-20250514\u0026#39; }; Building the AI Service @Injectable({ providedIn: \u0026#39;root\u0026#39; }) export class ClaudeService { private http = inject(HttpClient); askClaude(prompt: string): Observable\u0026lt;string\u0026gt; { return this.http.post\u0026lt;any\u0026gt;(environment.claudeApiUrl, { model: environment.claudeModel, max_tokens: 1024, messages: [{ role: \u0026#39;user\u0026#39;, content: prompt }] }).pipe( map(res =\u0026gt; res.content[0].text) ); } } The Signal-Based Component @Component({ selector: \u0026#39;app-ai-chat\u0026#39;, template: ` \u0026lt;div class=\u0026#34;ai-container\u0026#34;\u0026gt; \u0026lt;textarea [(ngModel)]=\u0026#34;userInput\u0026#34; placeholder=\u0026#34;Ask anything...\u0026#34;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;button (click)=\u0026#34;ask()\u0026#34; [disabled]=\u0026#34;isLoading()\u0026#34;\u0026gt; {{ isLoading() ? \u0026#39;Thinking...\u0026#39; : \u0026#39;Ask Claude\u0026#39; }} \u0026lt;/button\u0026gt; \u0026lt;div class=\u0026#34;response\u0026#34;\u0026gt;{{ aiResponse() }}\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; ` }) export class AiChatComponent { userInput = \u0026#39;\u0026#39;; aiResponse = signal(\u0026#39;\u0026#39;); isLoading = signal(false); private claude = inject(ClaudeService); ask() { this.isLoading.set(true); this.claude.askClaude(this.userInput).subscribe({ next: (res) =\u0026gt; { this.aiResponse.set(res); this.isLoading.set(false); } }); } } Conclusion Combining Angular 18 Signals with Claude API gives you a clean, reactive architecture for AI-powered features. The signal-based approach reduces boilerplate and makes state changes predictable.\nAt Techvia, we use this exact pattern in FleetIQ — our AI-first Transport Management System — for route optimization suggestions and document intelligence.\n","permalink":"https://blogs.techvia.software/posts/2025-05-27-angular-18-signals-claude-api/","summary":"\u003ch2 id=\"introduction\"\u003eIntroduction\u003c/h2\u003e\n\u003cp\u003eAngular 18 introduced a powerful reactivity model with \u003cstrong\u003eSignals\u003c/strong\u003e — and combined with the Claude API, it unlocks a new class of AI-first web applications. In this post, we\u0026rsquo;ll walk through building a reactive AI assistant component from scratch.\u003c/p\u003e\n\u003ch2 id=\"why-signals-for-ai-apps\"\u003eWhy Signals for AI Apps?\u003c/h2\u003e\n\u003cp\u003eTraditional Angular apps used RxJS observables for async state. While powerful, they add cognitive overhead when dealing with streaming AI responses. Signals simplify this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-typescript\" data-lang=\"typescript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Traditional RxJS approach\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"nx\"\u003eaiResponse$\u003c/span\u003e: \u003cspan class=\"kt\"\u003eObservable\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Signals approach — cleaner, more intuitive\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"nx\"\u003eaiResponse\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003esignal\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eisLoading\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003esignal\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003eboolean\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"setting-up-claude-api-in-angular\"\u003eSetting Up Claude API in Angular\u003c/h2\u003e\n\u003cp\u003eInstall the HTTP client and set up your environment:\u003c/p\u003e","title":"Building AI-First Apps with Angular 18 Signals and Claude API"},{"content":"Overview Deploying .NET Core 8 APIs manually is error-prone. Here\u0026rsquo;s how to set up a production-grade GitHub Actions pipeline that deploys to Azure App Service with zero downtime on every push to main.\nPrerequisites Azure App Service (Free or Basic tier) GitHub repository with your .NET Core 8 API Azure Service Principal or Publish Profile The GitHub Actions Workflow Create .github/workflows/deploy.yml:\nname: Deploy to Azure App Service on: push: branches: [ main ] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: \u0026#39;8.0.x\u0026#39; - name: Restore dependencies run: dotnet restore - name: Build run: dotnet build --configuration Release --no-restore - name: Test run: dotnet test --no-build --verbosity normal - name: Publish run: dotnet publish -c Release -o ./publish - name: Deploy to Azure uses: azure/webapps-deploy@v3 with: app-name: ${{ secrets.AZURE_APP_NAME }} publish-profile: ${{ secrets.AZURE_PUBLISH_PROFILE }} package: ./publish Setting Up Secrets In GitHub → Settings → Secrets:\nAZURE_APP_NAME = your-app-name AZURE_PUBLISH_PROFILE = \u0026lt;paste XML from Azure portal\u0026gt; Download publish profile: Azure Portal → App Service → Get Publish Profile.\nZero Downtime with Deployment Slots - name: Deploy to Staging Slot uses: azure/webapps-deploy@v3 with: app-name: ${{ secrets.AZURE_APP_NAME }} slot-name: staging publish-profile: ${{ secrets.AZURE_STAGING_PROFILE }} package: ./publish - name: Swap Staging to Production uses: azure/CLI@v2 with: inlineScript: | az webapp deployment slot swap \\ --resource-group ${{ secrets.AZURE_RG }} \\ --name ${{ secrets.AZURE_APP_NAME }} \\ --slot staging \\ --target-slot production Conclusion With this setup, every push to main triggers a full build, test, and zero-downtime deployment. At Techvia, this is our standard deployment pattern for all SaaS products including FleetIQ.\n","permalink":"https://blogs.techvia.software/posts/2025-05-26-dotnet-azure-app-service-ci-cd/","summary":"\u003ch2 id=\"overview\"\u003eOverview\u003c/h2\u003e\n\u003cp\u003eDeploying .NET Core 8 APIs manually is error-prone. Here\u0026rsquo;s how to set up a \u003cstrong\u003eproduction-grade GitHub Actions pipeline\u003c/strong\u003e that deploys to Azure App Service with zero downtime on every push to \u003ccode\u003emain\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"prerequisites\"\u003ePrerequisites\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eAzure App Service (Free or Basic tier)\u003c/li\u003e\n\u003cli\u003eGitHub repository with your .NET Core 8 API\u003c/li\u003e\n\u003cli\u003eAzure Service Principal or Publish Profile\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"the-github-actions-workflow\"\u003eThe GitHub Actions Workflow\u003c/h2\u003e\n\u003cp\u003eCreate \u003ccode\u003e.github/workflows/deploy.yml\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDeploy to Azure App Service\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\u003c/span\u003e\u003cspan class=\"nt\"\u003eon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003epush\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ebranches\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003emain ]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\u003c/span\u003e\u003cspan class=\"nt\"\u003ejobs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ebuild-and-deploy\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eSetup .NET\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/setup-dotnet@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003edotnet-version\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;8.0.x\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eRestore dependencies\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet restore\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eBuild\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet build --configuration Release --no-restore\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eTest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet test --no-build --verbosity normal\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ePublish\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet publish -c Release -o ./publish\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDeploy to Azure\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eazure/webapps-deploy@v3\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eapp-name\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e${{ secrets.AZURE_APP_NAME }}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003epublish-profile\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e${{ secrets.AZURE_PUBLISH_PROFILE }}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003epackage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e./publish\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"setting-up-secrets\"\u003eSetting Up Secrets\u003c/h2\u003e\n\u003cp\u003eIn GitHub → Settings → Secrets:\u003c/p\u003e","title":".NET Core 8 CI/CD to Azure App Service with GitHub Actions"},{"content":"The Challenge When we started building FleetIQ at Techvia, we had one rule: ship a working MVP in 30 days without compromising on architecture. Here\u0026rsquo;s how we did it.\nTech Stack Decision After evaluating several options, we settled on:\nLayer Technology Reason Frontend Angular 18 Team expertise, enterprise-grade Backend .NET Core 8 Performance, Azure native Database PostgreSQL Reliability, JSON support AI Claude API Best-in-class reasoning Cloud Azure Existing infrastructure Auth Azure AD B2C Enterprise SSO ready Architecture: Polyrepo Over Monorepo We chose a polyrepo structure early:\nfleetiq-api/ ← .NET Core 8 Web API fleetiq-web/ ← Angular 18 SPA fleetiq-shared/ ← Shared TypeScript types This paid off when we needed to scale the API independently during load testing.\nDay 1–7: Core API We prioritised the API first:\n// Clean architecture from day one FleetIQ.API/ Controllers/ FleetIQ.Application/ Commands/ Queries/ Handlers/ FleetIQ.Domain/ Entities/ Interfaces/ FleetIQ.Infrastructure/ Repositories/ Services/ Day 8–20: Angular Frontend With the API stable, we moved to Angular 18 with standalone components throughout:\n@Component({ standalone: true, imports: [CommonModule, RouterModule], selector: \u0026#39;app-fleet-dashboard\u0026#39;, templateUrl: \u0026#39;./fleet-dashboard.component.html\u0026#39; }) export class FleetDashboardComponent { vehicles = signal\u0026lt;Vehicle[]\u0026gt;([]); // ... } Day 21–28: Claude API Integration The AI layer was our differentiator. We integrated Claude API for:\nRoute optimization suggestions Document intelligence (driver docs, permits) Anomaly detection in fleet data Day 29–30: Azure Deployment CI/CD via GitHub Actions → Azure App Service. Took 4 hours to fully automate.\nLessons Learned Standalone components in Angular 18 saved significant boilerplate CQRS pattern in .NET made the codebase scale cleanly Claude API integration was faster than expected — 2 days for core features Polyrepo \u0026gt; monorepo for our team size and deployment independence What\u0026rsquo;s Next for FleetIQ We\u0026rsquo;re currently onboarding beta customers. If you\u0026rsquo;re managing employee transport or fleet operations, reach out to us.\n","permalink":"https://blogs.techvia.software/posts/2025-05-25-building-saas-mvp-angular-dotnet/","summary":"\u003ch2 id=\"the-challenge\"\u003eThe Challenge\u003c/h2\u003e\n\u003cp\u003eWhen we started building \u003cstrong\u003eFleetIQ\u003c/strong\u003e at Techvia, we had one rule: ship a working MVP in 30 days without compromising on architecture. Here\u0026rsquo;s how we did it.\u003c/p\u003e\n\u003ch2 id=\"tech-stack-decision\"\u003eTech Stack Decision\u003c/h2\u003e\n\u003cp\u003eAfter evaluating several options, we settled on:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eLayer\u003c/th\u003e\n          \u003cth\u003eTechnology\u003c/th\u003e\n          \u003cth\u003eReason\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eFrontend\u003c/td\u003e\n          \u003ctd\u003eAngular 18\u003c/td\u003e\n          \u003ctd\u003eTeam expertise, enterprise-grade\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eBackend\u003c/td\u003e\n          \u003ctd\u003e.NET Core 8\u003c/td\u003e\n          \u003ctd\u003ePerformance, Azure native\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eDatabase\u003c/td\u003e\n          \u003ctd\u003ePostgreSQL\u003c/td\u003e\n          \u003ctd\u003eReliability, JSON support\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eAI\u003c/td\u003e\n          \u003ctd\u003eClaude API\u003c/td\u003e\n          \u003ctd\u003eBest-in-class reasoning\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eCloud\u003c/td\u003e\n          \u003ctd\u003eAzure\u003c/td\u003e\n          \u003ctd\u003eExisting infrastructure\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eAuth\u003c/td\u003e\n          \u003ctd\u003eAzure AD B2C\u003c/td\u003e\n          \u003ctd\u003eEnterprise SSO ready\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"architecture-polyrepo-over-monorepo\"\u003eArchitecture: Polyrepo Over Monorepo\u003c/h2\u003e\n\u003cp\u003eWe chose a \u003cstrong\u003epolyrepo\u003c/strong\u003e structure early:\u003c/p\u003e","title":"How We Built a SaaS MVP in 30 Days with Angular 18 and .NET Core 8"}]