Build a Web App with ASP.NET Core 8.0 Web API and Angular 20 Using SQL Server

by Didin J. on Jul 10, 2025 Build a Web App with ASP.NET Core 8.0 Web API and Angular 20 Using SQL Server

Learn how to build a full-stack web app using ASP.NET Core 8.0, Angular 20, and SQL Server with modern practices like minimal APIs and standalone components.

In the ever-evolving world of web development, both ASP.NET Core and Angular have come a long way since their earlier versions. This updated guide brings a fresh take on the classic full-stack approach by combining ASP.NET Core 8.0 (with minimal APIs and Entity Framework Core) and Angular 20 (using standalone components and signals) to build a modern CRUD web application with SQL Server as the backend database.

In this step-by-step tutorial, you’ll learn how to:

  • Build a RESTful Web API using ASP.NET Core 8.0 and Entity Framework Core.

  • Create a responsive Angular 20 frontend using HttpClient to interact with the API.

  • Perform full CRUD operations (Create, Read, Update, Delete).

  • Connect the Angular frontend to the ASP.NET backend.

  • Run and test your full-stack application locally.

Whether you're modernizing an existing app or starting from scratch, this guide will give you a clean, up-to-date foundation.

Prerequisites

Make sure the following tools are installed on your machine before you begin:

🔧 Backend Requirements (ASP.NET Core 8.0)

  • .NET 8 SDK

  • SQL Server (LocalDB, Developer, or Express Edition). We are using Docker version. 

    docker pull mcr.microsoft.com/mssql/server:2022-latest
    
    docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=YourStrongPassword1" -p 1433:1433 --name sqlserver -d mcr.microsoft.com/mssql/server:2022-latest
  • IDE: Visual Studio 2022+ (with ASP.NET & web development workload) or Visual Studio Code

💻 Frontend Requirements (Angular 20)

  • Node.js 20+

  • Angular CLI v20: 

    npm install -g @angular/cli
  • Code Editor: VS Code is recommended

📦 General

  • Basic familiarity with C#, TypeScript, and Angular

  • Command line (Terminal, Command Prompt, or PowerShell)


Set Up ASP.NET Core 8.0 Web API

We’ll start by creating a RESTful API using ASP.NET Core 8.0 with Entity Framework Core and SQL Server for data storage.

1. 🛠 Create a New ASP.NET Core Web API Project

Open a terminal or use Visual Studio/VS Code and run:

dotnet new webapi -n ProductApi
cd ProductApi

This scaffolds a minimal API template with weather forecast samples, which we’ll replace with a custom Product API.

2. 🧱 Install EF Core SQL Server Provider

Inside the ProductApi folder, install Entity Framework Core and the SQL Server provider:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

3. 📦 Create the Product Model

Create a new folder called Models and add Product.cs:

using Microsoft.EntityFrameworkCore;

namespace ProductApi.Models;

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    [Precision(18, 2)]
    public decimal Price { get; set; }
}

4. 🧩 Create the EF Core DbContext

Create a Data folder and add AppDbContext.cs:

using Microsoft.EntityFrameworkCore;
using ProductApi.Models;

namespace ProductApi.Data;

public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
    public DbSet<Product> Products => Set<Product>();
}

5. 🔧 Configure the Database Connection

Open appsettings.json and add your SQL Server connection string:

"ConnectionStrings": {
  "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=ProductDb;Trusted_Connection=True;"
}

Then open Program.cs and update it:

using Microsoft.EntityFrameworkCore;
using ProductApi.Data;

var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Enable Swagger
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// Minimal CRUD endpoints will go here
app.UseHttpsRedirection();

app.Run();


Implement CRUD API Endpoints (Minimal APIs)

We’ll now define routes to Create, Read, Update, and Delete Product records.

1. ✏️ Add Minimal API Endpoints in Program.cs

At the bottom of Program.cs, after app.UseHttpsRedirection();, insert the following code:

using ProductApi.Models;
using ProductApi.Data;

app.MapGet("/api/products", async (AppDbContext db) =>
    await db.Products.ToListAsync());

app.MapGet("/api/products/{id:int}", async (int id, AppDbContext db) =>
    await db.Products.FindAsync(id) is Product product
        ? Results.Ok(product)
        : Results.NotFound());

app.MapPost("/api/products", async (Product product, AppDbContext db) =>
{
    db.Products.Add(product);
    await db.SaveChangesAsync();
    return Results.Created($"/api/products/{product.Id}", product);
});

app.MapPut("/api/products/{id:int}", async (int id, Product updatedProduct, AppDbContext db) =>
{
    var product = await db.Products.FindAsync(id);
    if (product is null) return Results.NotFound();

    product.Name = updatedProduct.Name;
    product.Description = updatedProduct.Description;
    product.Price = updatedProduct.Price;

    await db.SaveChangesAsync();
    return Results.NoContent();
});

app.MapDelete("/api/products/{id:int}", async (int id, AppDbContext db) =>
{
    var product = await db.Products.FindAsync(id);
    if (product is null) return Results.NotFound();

    db.Products.Remove(product);
    await db.SaveChangesAsync();
    return Results.NoContent();
});

Apply EF Core Migrations and Create the Database

Run the following commands in your terminal:

dotnet ef migrations add InitialCreate
dotnet ef database update

If you're using Visual Studio, you can also run these from the Package Manager Console.

This will generate the Migrations folder and create the ProductDb database in SQL Server.

Test the API

1. Run the app:

dotnet run

2. Open your browser and navigate to:

http://localhost:5104/swagger

You’ll see a full Swagger UI with your GET, POST, PUT, and DELETE routes for /api/products.

Build a Web App with ASP.NET Core 8.0 Web API and Angular 20 Using SQL Server - Swagger UI

Enable CORS for Angular 20

To allow your Angular frontend (usually served on http://localhost:4200) to make API requests to your backend (https://localhost:5001), you need to enable CORS in your ASP.NET Core app.

1. In Program.cs, register the CORS service:

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAngularApp", policy =>
    {
        policy.WithOrigins("http://localhost:4200")
              .AllowAnyHeader()
              .AllowAnyMethod();
    });
});

2. Add the CORS middleware before app.Run():

app.UseCors("AllowAngularApp");

Final Program.cs (essential parts):

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAngularApp", policy =>
    {
        policy.WithOrigins("http://localhost:4200")
              .AllowAnyHeader()
              .AllowAnyMethod();
    });
});

...

var app = builder.Build();

...

app.UseCors("AllowAngularApp");

app.Run();

With CORS enabled, your Angular app can now safely consume the API.


Step-by-Step: Create an Angular 20 Frontend for Product CRUD

1. 🧱 Create a New Angular 20 App

In a separate folder:

ng new product-frontend --standalone --routing --style=css
cd product-frontend

Serve the app to confirm:

ng serve

Open your browser to http://localhost:4200

Build a Web App with ASP.NET Core 8.0 Web API and Angular 20 Using SQL Server - Angular Home

2. 📦 Generate a Product Service

ng generate service services/product.services

Edit src/app/services/product.ts:

import { HttpClient } from '@angular/common/http';
import { computed, Injectable, signal } from '@angular/core';
import { tap } from 'rxjs';
import { Product } from '../models/product.model';

@Injectable({
  providedIn: 'root'
})
export class ProductServices {
  private baseUrl = 'https://localhost:5001/api/products';
  private _products = signal<Product[]>([]);

  products = computed(() => this._products());

  constructor(private http: HttpClient) { }

  getAll() {
    return this.http.get<Product[]>(this.baseUrl).pipe(
      tap(data => this._products.set(data))
    );
  }

  getById(id: number) {
    return this.http.get<Product>(`${this.baseUrl}/${id}`);
  }

  create(product: Product) {
    return this.http.post<Product>(this.baseUrl, product).pipe(
      tap(() => this.getAll().subscribe()) // auto-refresh
    );
  }

  update(product: Product) {
    return this.http.put(`${this.baseUrl}/${product.id}`, product).pipe(
      tap(() => this.getAll().subscribe())
    );
  }

  delete(id: number) {
    return this.http.delete(`${this.baseUrl}/${id}`).pipe(
      tap(() => this.getAll().subscribe())
    );
  }
}

3. 🧩 Create Product Model

Create src/app/models/product.model.ts:

export interface Product {
  id?: number;
  name: string;
  description: string;
  price: number;
}

4. 🧱 Generate Product Component

ng generate component components/product.component --standalone --flat

Then update src/app/components/product.component.ts:

import { Component, OnInit } from '@angular/core';
import { ProductServices } from '../services/product.services';
import { Product } from '../models/product.model';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-product.component',
  imports: [CommonModule, FormsModule],
  templateUrl: './product.component.html',
  styleUrl: './product.component.css'
})
export class ProductComponent implements OnInit {
  get products() {
    return this.productService.products();
  }

  newProduct: Product = { name: '', description: '', price: 0 };
  selectedProduct: Product | null = null;

  constructor(public productService: ProductServices) { }

  ngOnInit() {
    this.productService.getAll().subscribe();
  }

  save() {
    if (this.selectedProduct) {
      this.productService.update(this.selectedProduct).subscribe(() => this.selectedProduct = null);
    } else {
      this.productService.create(this.newProduct).subscribe(() => this.newProduct = { name: '', description: '', price: 0 });
    }
  }

  edit(product: Product) {
    this.selectedProduct = { ...product };
  }

  delete(id: number) {
    this.productService.delete(id).subscribe();
  }
}

5. 🖼️ Create Product Component Template

Create src/app/components/product.component.html:

<h2>Product Manager</h2>

<form (ngSubmit)="save()">
  <div *ngIf="selectedProduct; else newForm">
    <input
      type="text"
      [(ngModel)]="selectedProduct.name"
      name="name"
      placeholder="Name"
      required
    />
    <input
      type="text"
      [(ngModel)]="selectedProduct.description"
      name="description"
      placeholder="Description"
      required
    />
    <input
      type="number"
      [(ngModel)]="selectedProduct.price"
      name="price"
      placeholder="Price"
      required
    />
    <button type="submit">Update</button>
  </div>

  <ng-template #newForm>
    <input
      type="text"
      [(ngModel)]="newProduct.name"
      name="name"
      placeholder="Name"
      required
    />
    <input
      type="text"
      [(ngModel)]="newProduct.description"
      name="description"
      placeholder="Description"
      required
    />
    <input
      type="number"
      [(ngModel)]="newProduct.price"
      name="price"
      placeholder="Price"
      required
    />
    <button type="submit">Add</button>
  </ng-template>
</form>

<ul>
  <li *ngFor="let product of products">
    {{ product.name }} - {{ product.price | currency }}
    <button (click)="edit(product)">Edit</button>
    <button (click)="delete(product.id!)">Delete</button>
  </li>
</ul>

6.  Add a Route for ProductComponent

In src/app/app.routes.ts:

import { Routes } from '@angular/router';
import { ProductComponent } from './components/product.component';

export const routes: Routes = [
  {
    path: '',
    component: ProductComponent
  }
];

You can add a layout wrapper or more pages later — this makes ProductComponent your root view.

7. Update AppComponent Template to show only Router View

In src/app/app.html:

<router-outlet />

7. Update app/app.config.ts to add HttpClient

import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideHttpClient()
  ]
};

🔐 Bonus (Optional): Ignore SSL Certificate in Dev (for local HTTPS API)

If your browser/API complains about self-signed HTTPS certs, you can:

  1. Allow self-signed certs in the browser

  2. Or run ASP.NET Core backend with HTTP (--urls http://localhost:5000 in Program.cs)


Final Testing Guide

1. Run the ASP.NET Core Backend

From the ProductApi directory:

dotnet run

Ensure it's accessible at:

https://localhost:5104/swagger

Test the following endpoints via Swagger:

  • GET /api/products

  • POST /api/products

  • PUT /api/products/{id}

  • DELETE /api/products/{id}

If everything works here, your backend is ready.

2. Run the Angular 20 Frontend

From the product-frontend directory:

ng serve

Open:
http://localhost:4200

You should see:

  • A form to create products

  • A list of existing products

  • Working Edit and Delete buttons

  • Data syncing automatically after every change

Make sure the browser does not block any API requests due to CORS or HTTPS certificate issues.

3. End-to-End Scenarios

Test the full workflow:

✅ Add a new product
✅ Edit a product
✅ Delete a product
✅ Refresh the page — data should persist
✅ Check the SQL Server database (e.g., using Azure Data Studio)

If all these work, your app is fully functional!


Conclusion

In this updated tutorial, we built a full-stack web app using:

  • ASP.NET Core 8.0 (with minimal APIs + Entity Framework Core)

  • Angular 20 (standalone components, Signals, HttpClient)

  • SQL Server (via Docker on macOS)

You learned how to:

  • Build a modern RESTful API with minimal boilerplate in .NET 8

  • Connect to SQL Server using EF Core

  • Build an Angular 20 app using standalone components and Signals

  • Enable CORS for local development

  • Deploy and test on macOS with Docker

This architecture is scalable, maintainable, and production-ready with some refinements like authentication and validation.

You can find the fully working source code on our GitHub.

That's just the basics. If you need more deep learning about ASP.NET Core, Angular, or related, you can take the following cheap course:

Thanks!