Introduction
Building a simple CRUD application is one of the best ways to understand how all layers of a full-stack application work together. CRUD stands for Create, Read, Update, and Delete. In this tutorial, we will build a complete CRUD system using:
This guide walks you step-by-step through the entire setup: database design, API development, Angular UI, service integration, and testing CRUD operations.
Application Architecture Overview
The full-stack flow looks like this:
Angular UI → Angular Service → ASP.NET Core API → EF Core → SQL Server
↑ ↓
←————— JSON Response ←————————— API Controller ←—————————
Each layer has a clear responsibility:
SQL Server stores the data
Entity Framework Core manages the ORM
ASP.NET Core exposes APIs
Angular interacts with the API and displays UI
1. Setting Up the Database
Create the SQL Table
We will build a simple "Employees" CRUD system.
Run this SQL script:
CREATE TABLE Employees (
Id INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(100),
Email NVARCHAR(100),
Department NVARCHAR(50)
);
This table will store our data.
2. Building the ASP.NET Core API
Create a New Project
Run:
dotnet new webapi -n CrudApi
cd CrudApi
Add Entity Framework Core and SQL Server Provider
Install packages:
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
Create the DB Context
Create AppDbContext.cs:
using Microsoft.EntityFrameworkCore;
namespace CrudApi.Data
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Employee> Employees { get; set; }
}
}
Create Employee Model
Create Employee.cs:
namespace CrudApi.Models
{
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Department { get; set; }
}
}
Add Connection String in appsettings.json
"ConnectionStrings": {
"DefaultConnection": "Server=YOUR_SERVER;Database=CrudDb;Trusted_Connection=True;TrustServerCertificate=True;"
}
Register SQL Server in Program.cs
builder.Services.AddDbContext<AppDbContext>(
options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))
);
Create the CRUD Controller
Create EmployeesController.cs:
[ApiController]
[Route("api/[controller]")]
public class EmployeesController : ControllerBase
{
private readonly AppDbContext _context;
public EmployeesController(AppDbContext context)
{
_context = context;
}
// GET All
[HttpGet]
public async Task<IActionResult> Get()
{
return Ok(await _context.Employees.ToListAsync());
}
// GET by Id
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
var emp = await _context.Employees.FindAsync(id);
return emp == null ? NotFound() : Ok(emp);
}
// POST Create
[HttpPost]
public async Task<IActionResult> Post(Employee emp)
{
_context.Employees.Add(emp);
await _context.SaveChangesAsync();
return Ok(emp);
}
// PUT Update
[HttpPut("{id}")]
public async Task<IActionResult> Put(int id, Employee emp)
{
var dbEmp = await _context.Employees.FindAsync(id);
if (dbEmp == null) return NotFound();
dbEmp.Name = emp.Name;
dbEmp.Email = emp.Email;
dbEmp.Department = emp.Department;
await _context.SaveChangesAsync();
return Ok(dbEmp);
}
// DELETE
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var emp = await _context.Employees.FindAsync(id);
if (emp == null) return NotFound();
_context.Employees.Remove(emp);
await _context.SaveChangesAsync();
return Ok();
}
}
Your API is now ready.
3. Building the Angular Frontend
Create Angular Project
ng new crud-ui
cd crud-ui
Install Angular Material (optional):
ng add @angular/material
Create Employee Model
Create employee.model.ts:
export interface Employee {
id?: number;
name: string;
email: string;
department: string;
}
Create an Employee Service
ng generate service services/employee
employee.service.ts:
@Injectable({
providedIn: 'root'
})
export class EmployeeService {
apiUrl = 'https://localhost:5001/api/employees';
constructor(private http: HttpClient) {}
getEmployees() {
return this.http.get<Employee[]>(this.apiUrl);
}
addEmployee(emp: Employee) {
return this.http.post<Employee>(this.apiUrl, emp);
}
updateEmployee(emp: Employee) {
return this.http.put<Employee>(`${this.apiUrl}/${emp.id}`, emp);
}
deleteEmployee(id: number) {
return this.http.delete(`${this.apiUrl}/${id}`);
}
}
4. Creating Components for CRUD
Generate Components
ng generate component pages/employee-list
ng generate component pages/employee-form
Employee List Component
employee-list.component.ts:
export class EmployeeListComponent implements OnInit {
employees: Employee[] = [];
constructor(private service: EmployeeService) {}
ngOnInit(): void {
this.loadEmployees();
}
loadEmployees() {
this.service.getEmployees().subscribe(res => this.employees = res);
}
delete(id: number) {
this.service.deleteEmployee(id).subscribe(() => this.loadEmployees());
}
}
employee-list.component.html:
<h3>Employee List</h3>
<table>
<tr>
<th>Name</th>
<th>Email</th>
<th>Department</th>
<th>Actions</th>
</tr>
<tr *ngFor="let emp of employees">
<td>{{ emp.name }}</td>
<td>{{ emp.email }}</td>
<td>{{ emp.department }}</td>
<td>
<button (click)="delete(emp.id!)">Delete</button>
</td>
</tr>
</table>
<a routerLink="/add">Add Employee</a>
Employee Form Component
employee-form.component.ts:
export class EmployeeFormComponent {
employee: Employee = {
name: '',
email: '',
department: ''
};
constructor(private service: EmployeeService, private router: Router) {}
save() {
this.service.addEmployee(this.employee).subscribe(() => {
this.router.navigate(['/']);
});
}
}
employee-form.component.html:
<h3>Add Employee</h3>
<form (ngSubmit)="save()">
<input [(ngModel)]="employee.name" name="name" placeholder="Name">
<input [(ngModel)]="employee.email" name="email" placeholder="Email">
<input [(ngModel)]="employee.department" name="department" placeholder="Department">
<button type="submit">Save</button>
</form>
5. Setting Up Angular Routing
Add routes in app-routing.module.ts:
const routes: Routes = [
{ path: '', component: EmployeeListComponent },
{ path: 'add', component: EmployeeFormComponent }
];
6. Testing the CRUD App
Once both the backend and frontend are running:
Run API: dotnet run
Run Angular: ng serve
Test all operations:
Add a new employee
View all employees
Update an employee
Delete an employee
Everything should be fully functional.
7. Best Practices for Production
Use DTOs instead of exposing entity models directly
Add client-side and server-side validation
Use Angular Material or PrimeNG for better UI
Secure the API with JWT authentication
Implement global error handling in Angular and ASP.NET Core
Use environment variables for API URLs
Add pagination to large datasets
Create a repository layer for cleaner architecture
Conclusion
You have now built a fully functional CRUD application using:
This architecture is widely used in enterprise-grade applications. By understanding the flow between the UI, API, and database, you gain the foundation needed for building large, scalable systems.