End-to-End Connection — Verifying the Angular to API Pipeline

With CORS configured, environments set up, and the API client service built, the final step is verifying the complete pipeline end-to-end — running both servers, confirming requests flow through correctly, and debugging any issues that arise. A health-check component that displays the API status and a functional post list that fetches real data from the database confirm the entire stack is working: Angular → HTTP → ASP.NET Core → EF Core → SQL Server → response back to the UI.

Running Both Servers and Verifying the Connection

// ── ASP.NET Core: health check endpoint ───────────────────────────────────
// Program.cs:
builder.Services.AddHealthChecks()
    .AddDbContextCheck<AppDbContext>("database")
    .AddCheck("api", () => HealthCheckResult.Healthy("API is running"));

app.MapHealthChecks("/api/health", new HealthCheckOptions {
    ResponseWriter = async (ctx, report) => {
        ctx.Response.ContentType = "application/json";
        var result = JsonSerializer.Serialize(new {
            status  = report.Status.ToString(),
            version = "1.0.0",
            checks  = report.Entries.ToDictionary(
                e => e.Key,
                e => new { status = e.Value.Status.ToString(), description = e.Value.Description }
            ),
            timestamp = DateTime.UtcNow,
        });
        await ctx.Response.WriteAsync(result);
    }
});
// Response: { "status": "Healthy", "version": "1.0.0",
//             "checks": { "database": { "status": "Healthy" }, ... } }
// ── Angular: health check component ──────────────────────────────────────
interface HealthStatus {
  status:    'Healthy' | 'Degraded' | 'Unhealthy';
  version:   string;
  checks:    Record<string, { status: string; description: string }>;
  timestamp: string;
}

@Component({
  selector:   'app-api-health',
  standalone:  true,
  imports:    [AsyncPipe, DatePipe],
  template: `
    @if (health$ | async; as health) {
      <div class="health-badge" [class]="'health-' + health.status.toLowerCase()">
        API {{ health.status }} · v{{ health.version }}
        · DB: {{ health.checks['database']?.status }}
        · {{ health.timestamp | date:'HH:mm:ss' }}
      </div>
    } @else {
      <div class="health-badge health-checking">Checking API...</div>
    }
  `,
})
export class ApiHealthComponent {
  private http    = inject(HttpClient);
  private config  = inject(APP_CONFIG);

  health$ = this.http.get<HealthStatus>(`${this.config.apiUrl}/api/health`)
    .pipe(catchError(() => of({ status: 'Unhealthy' as const,
                                 version: '?', checks: {}, timestamp: new Date().toISOString() })));
}

// ── Post list component — end-to-end verification ────────────────────────
@Component({
  selector:   'app-post-list',
  standalone:  true,
  imports:    [AsyncPipe],
  template: `
    @if (loading()) { <app-spinner /> }
    @if (error()) { <p class="error">{{ error() }}</p> }
    @for (post of posts(); track post.id) {
      <app-post-card [post]="post" />
    } @empty {
      @if (!loading()) { <p>No posts found.</p> }
    }
  `,
})
export class PostListComponent implements OnInit {
  private postsApi   = inject(PostsApiService);
  private destroyRef = inject(DestroyRef);

  posts   = signal<PostSummaryDto[]>([]);
  loading = signal(true);
  error   = signal<string | null>(null);

  ngOnInit() {
    this.postsApi.getPublished().pipe(
      takeUntilDestroyed(this.destroyRef),
      finalize(() => this.loading.set(false)),
    ).subscribe({
      next:  result => this.posts.set(result.items),
      error: (err: ApiError) => this.error.set(err.message),
    });
  }
}
Note: To run both servers simultaneously during development: open two terminal windows — one runs cd BlogApp.Api && dotnet run (API on port 5000), the other runs cd blog-app && ng serve (Angular on port 4200). The proxy configuration routes Angular’s API calls to the ASP.NET Core server. Alternatively, use a VS Code compound launch configuration (launch.json with "compounds") to start both with a single F5 press — or use the concurrently npm package: "start:all": "concurrently \"dotnet run --project BlogApp.Api\" \"ng serve\"".
Tip: Browser DevTools Network tab is your primary debugging tool for the Angular ↔ API connection. Check: the request URL (correct API route?), request headers (Authorization header present? Content-Type: application/json?), response status (200, 401, 404, 500?), response headers (Access-Control-Allow-Origin present if not using proxy?), and response body (correct JSON structure?). The “Preview” tab shows the parsed JSON response; “Response” shows the raw text. Almost every connection issue is diagnosable within 2 minutes in the Network tab.
Warning: The development health check endpoint (/api/health) should be protected in production or return minimal information. A detailed health check that lists database status, version numbers, and internal check details is useful for DevOps but gives attackers information about your infrastructure. In production, either require authentication ([Authorize] on the health check endpoint) or return only the overall status without details for unauthenticated callers.

Common Mistakes

Mistake 1 — CORS error persisting despite configuration (middleware order wrong)

❌ Wrong — UseCors() after UseAuthentication(); 401 responses lack CORS headers; browser shows CORS error for auth failures.

✅ Correct — UseRouting() → UseCors() → UseAuthentication(); all responses (including errors) get CORS headers.

Mistake 2 — Not verifying with Network tab (guessing at the problem)

❌ Wrong — console.log debugging in Angular without checking the actual HTTP request/response.

✅ Correct — always check DevTools Network tab first; the actual request and response contain everything needed for diagnosis.

🧠 Test Yourself

The browser DevTools Network tab shows the API request returning 200 OK with the correct JSON, but the Angular component shows “No posts found.” What is the most likely cause?