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),
});
}
}
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\""./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.