Angular HttpClient: GET, POST, PUT, DELETE
Angular uses HttpClient to communicate with an ASP.NET Core Web API.
The four most common operations are:
| Operation | Angular | Purpose | ASP.NET Core |
|---|---|---|---|
| GET | http.get() |
Read | [HttpGet] |
| POST | http.post() |
Create | [HttpPost] |
| PUT | http.put() |
Update | [HttpPut] |
| DELETE | http.delete() |
Delete | [HttpDelete] |
These are commonly called CRUD operations:
Create → POST
Read → GET
Update → PUT
Delete → DELETE
Full Simple Sample Program
Suppose our ASP.NET Core Web API exposes:
GET /api/products
GET /api/products/1
POST /api/products
PUT /api/products/1
DELETE /api/products/1
1. app.config.ts
First configure HttpClient:
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient()
]
};
2. product.ts
export interface Product {
id: number;
name: string;
price: number;
}
3. product.service.ts
This contains all four HTTP operations.
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Product } from './product';
@Injectable({
providedIn: 'root'
})
export class ProductService {
private http = inject(HttpClient);
private apiUrl =
'https://localhost:7001/api/products';
// GET - Get all products
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(
this.apiUrl
);
}
// GET - Get one product
getProduct(id: number): Observable<Product> {
return this.http.get<Product>(
`${this.apiUrl}/${id}`
);
}
// POST - Create product
addProduct(product: Product): Observable<Product> {
return this.http.post<Product>(
this.apiUrl,
product
);
}
// PUT - Update product
updateProduct(
id: number,
product: Product
): Observable<void> {
return this.http.put<void>(
`${this.apiUrl}/${id}`,
product
);
}
// DELETE - Delete product
deleteProduct(id: number): Observable<void> {
return this.http.delete<void>(
`${this.apiUrl}/${id}`
);
}
}
This service is the main code to remember.
1. How Do You Perform a GET Request?
What is GET?
GET is used to retrieve/read data from the server.
For example:
GET /api/products
means:
Give me all products.
Angular Syntax
this.http.get<T>(url);
Here T represents the expected response type.
For example:
this.http.get<Product[]>(url);
means we expect an array of products.
Get All Products
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(
this.apiUrl
);
}
Angular sends:
GET https://localhost:7001/api/products
ASP.NET Core might have:
[HttpGet]
public ActionResult<IEnumerable<Product>> GetProducts()
{
return Ok(products);
}
The API returns:
[
{
"id": 1,
"name": "Laptop",
"price": 75000
},
{
"id": 2,
"name": "Mouse",
"price": 1200
}
]
The flow is:
Angular
↓
http.get<Product[]>()
↓
GET /api/products
↓
ASP.NET Core [HttpGet]
↓
JSON response
↓
Observable<Product[]>
GET a Single Product
If we want product 10:
getProduct(id: number): Observable<Product> {
return this.http.get<Product>(
`${this.apiUrl}/${id}`
);
}
Calling:
this.productService.getProduct(10);
produces:
GET https://localhost:7001/api/products/10
ASP.NET Core:
[HttpGet("{id}")]
public ActionResult<Product> GetProduct(int id)
{
// Find product
return Ok(product);
}
2. How Do You Perform a POST Request?
What is POST?
POST is normally used to create a new resource.
For example:
POST /api/products
means:
Create a new product.
Angular Syntax
this.http.post<T>(
url,
body
);
There are two important parameters:
url
↓
Where should the request go?
body
↓
What data should be sent?
Example
addProduct(product: Product): Observable<Product> {
return this.http.post<Product>(
this.apiUrl,
product
);
}
Suppose:
const product: Product = {
id: 0,
name: 'Monitor',
price: 15000
};
and we call:
this.productService.addProduct(product);
Angular sends conceptually:
POST https://localhost:7001/api/products
Content-Type: application/json
with a request body like:
{
"id": 0,
"name": "Monitor",
"price": 15000
}
ASP.NET Core:
[HttpPost]
public ActionResult<Product> AddProduct(Product product)
{
// Save product
return Ok(product);
}
Flow:
Angular Product object
↓
http.post()
↓
POST /api/products
↓
JSON request body
↓
ASP.NET Core [HttpPost]
↓
Create product
↓
JSON response
3. How Do You Perform a PUT Request?
What is PUT?
PUT is commonly used to update an existing resource.
For example:
PUT /api/products/10
means:
Update product 10.
Angular Syntax
this.http.put<T>(
url,
body
);
Like POST, PUT usually sends a request body.
Example
updateProduct(
id: number,
product: Product
): Observable<void> {
return this.http.put<void>(
`${this.apiUrl}/${id}`,
product
);
}
Suppose:
const product: Product = {
id: 10,
name: 'Gaming Monitor',
price: 25000
};
Call:
this.productService.updateProduct(
10,
product
);
Angular sends:
PUT https://localhost:7001/api/products/10
Content-Type: application/json
Body:
{
"id": 10,
"name": "Gaming Monitor",
"price": 25000
}
ASP.NET Core:
[HttpPut("{id}")]
public IActionResult UpdateProduct(
int id,
Product product)
{
// Update product
return NoContent();
}
Flow:
Updated Product object
↓
http.put()
↓
PUT /api/products/10
↓
JSON request body
↓
ASP.NET Core [HttpPut]
↓
Update product
↓
204 No Content
4. How Do You Perform a DELETE Request?
What is DELETE?
DELETE is used to delete a resource.
For example:
DELETE /api/products/10
means:
Delete product 10.
Angular Syntax
this.http.delete<T>(url);
Unlike POST and PUT, a typical REST DELETE request identifies the resource through the URL and does not require a request body.
Example
deleteProduct(id: number): Observable<void> {
return this.http.delete<void>(
`${this.apiUrl}/${id}`
);
}
Calling:
this.productService.deleteProduct(10);
sends:
DELETE https://localhost:7001/api/products/10
ASP.NET Core:
[HttpDelete("{id}")]
public IActionResult DeleteProduct(int id)
{
// Delete product
return NoContent();
}
Flow:
Product ID
↓
deleteProduct(10)
↓
http.delete()
↓
DELETE /api/products/10
↓
ASP.NET Core [HttpDelete]
↓
Delete product
↓
204 No Content
Using These Methods From a Component
The service returns Observables, so the component subscribes to them.
GET
loadProducts(): void {
this.productService
.getProducts()
.subscribe({
next: (data) => {
this.products = data;
},
error: (error) => {
console.error(error);
}
});
}
POST
addProduct(): void {
const product: Product = {
id: 0,
name: 'Monitor',
price: 15000
};
this.productService
.addProduct(product)
.subscribe({
next: (result) => {
console.log('Created:', result);
},
error: (error) => {
console.error(error);
}
});
}
PUT
updateProduct(): void {
const product: Product = {
id: 10,
name: 'Gaming Monitor',
price: 25000
};
this.productService
.updateProduct(10, product)
.subscribe({
next: () => {
console.log('Product updated');
},
error: (error) => {
console.error(error);
}
});
}
DELETE
deleteProduct(id: number): void {
this.productService
.deleteProduct(id)
.subscribe({
next: () => {
console.log('Product deleted');
},
error: (error) => {
console.error(error);
}
});
}
Complete CRUD Flow
For an Angular + ASP.NET Core application:
Angular
│
ProductService
│
HttpClient
│
┌─────────────┼──────────────┐
│ │ │
GET POST PUT DELETE
│ │ │ │
↓ ↓ ↓ ↓
Read Create Update Delete
│ │ │ │
└─────────────┼──────────────┼──────────────┘
↓
ASP.NET Core API
↓
EF Core
↓
SQL Server
Important Syntax to Remember
For interviews, these four lines are the most important:
// GET
this.http.get<Product[]>(url);
// POST
this.http.post<Product>(url, product);
// PUT
this.http.put<void>(url, product);
// DELETE
this.http.delete<void>(url);
Or even more simply:
GET
http.get(url)
POST
http.post(url, body)
PUT
http.put(url, body)
DELETE
http.delete(url)
Notice the pattern:
GET → URL
POST → URL + BODY
PUT → URL + BODY
DELETE → URL
Why Observable<void> for PUT and DELETE?
You may have noticed:
Observable<void>
for:
updateProduct()
deleteProduct()
That's because an ASP.NET Core API often returns:
return NoContent();
which produces:
HTTP 204 No Content
There is no response body.
Therefore:
Observable<void>
is appropriate.
If your API returns the updated product instead, you could use:
Observable<Product>
For example:
return this.http.put<Product>(
`${this.apiUrl}/${id}`,
product
);
The TypeScript response type should match the API's response contract.
Key Points
- Angular uses
HttpClientfor HTTP requests. GETretrieves data.POSTnormally creates data.PUTnormally updates/replaces an existing resource.DELETEdeletes a resource.POSTandPUTnormally send a request body.GETand typicalDELETErequests generally identify data through the URL.- Angular
HttpClientmethods return RxJSObservables. - The generic type specifies the expected response:
get<Product[]>()
post<Product>()
put<void>()
delete<void>()
- A clean Angular structure is:
Component
↓
Service
↓
HttpClient
↓
ASP.NET Core Web API
Interview Questions and Answers
1. How do you perform a GET request in Angular?
Use HttpClient.get():
return this.http.get<Product[]>(
'/api/products'
);
It is used to retrieve data from the API.
2. How do you perform a POST request?
Use HttpClient.post():
return this.http.post<Product>(
'/api/products',
product
);
POST is commonly used to create a new resource.
3. How do you perform a PUT request?
Use HttpClient.put():
return this.http.put<void>(
`/api/products/${id}`,
product
);
PUT is commonly used to update an existing resource.
4. How do you perform a DELETE request?
Use HttpClient.delete():
return this.http.delete<void>(
`/api/products/${id}`
);
It sends an HTTP DELETE request to remove the specified resource.
5. What do Angular HttpClient methods return?
They return RxJS Observables.
For example:
Observable<Product[]>
6. What is the difference between POST and PUT?
POST is commonly used to create a new resource:
POST /api/products
PUT is commonly used to update/replace a known resource:
PUT /api/products/10
7. What does <Product[]> mean here?
this.http.get<Product[]>(url);
It tells TypeScript that the application expects the response to have the shape of an array of Product objects.
Short Interview Answer
Angular performs CRUD operations using
HttpClient:get()retrieves data,post()creates data,put()updates data, anddelete()removes data. These methods send HTTP requests to the ASP.NET Core Web API and return RxJS Observables containing the response.
Below is a complete Angular standalone example showing HttpClient with GET, POST, PUT, and DELETE. It assumes an API exists at https://localhost:7001/api/products.
Project files
src/
├── app/
│ ├── app.ts
│ ├── app.html
│ ├── app.css
│ ├── product.ts
│ └── product.service.ts
├── app.config.ts
└── main.ts
1. product.ts
export interface Product {
id: number;
name: string;
price: number;
}
2. product.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Product } from './product';
@Injectable({
providedIn: 'root'
})
export class ProductService {
private http = inject(HttpClient);
private apiUrl = 'https://localhost:7001/api/products';
// GET - Get all products
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(this.apiUrl);
}
// GET - Get product by ID
getProduct(id: number): Observable<Product> {
return this.http.get<Product>(`${this.apiUrl}/${id}`);
}
// POST - Create product
createProduct(product: Product): Observable<Product> {
return this.http.post<Product>(
this.apiUrl,
product
);
}
// PUT - Update product
updateProduct(id: number, product: Product): Observable<void> {
return this.http.put<void>(
`${this.apiUrl}/${id}`,
product
);
}
// DELETE - Delete product
deleteProduct(id: number): Observable<void> {
return this.http.delete<void>(
`${this.apiUrl}/${id}`
);
}
}
3. app.config.ts
In a standalone Angular application, configure HttpClient using provideHttpClient().
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient()
]
};
4. app.ts
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProductService } from './product.service';
import { Product } from './product';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule],
templateUrl: './app.html',
styleUrl: './app.css'
})
export class App implements OnInit {
private productService = inject(ProductService);
products: Product[] = [];
ngOnInit(): void {
this.loadProducts();
}
// GET
loadProducts(): void {
this.productService.getProducts().subscribe({
next: (data) => {
this.products = data;
},
error: (error) => {
console.error('GET Error:', error);
}
});
}
// POST
addProduct(): void {
const product: Product = {
id: 0,
name: 'Laptop',
price: 75000
};
this.productService.createProduct(product).subscribe({
next: (createdProduct) => {
console.log('Product created:', createdProduct);
this.loadProducts();
},
error: (error) => {
console.error('POST Error:', error);
}
});
}
// PUT
updateProduct(id: number): void {
const product: Product = {
id: id,
name: 'Gaming Laptop',
price: 95000
};
this.productService.updateProduct(id, product).subscribe({
next: () => {
console.log('Product updated');
this.loadProducts();
},
error: (error) => {
console.error('PUT Error:', error);
}
});
}
// DELETE
deleteProduct(id: number): void {
this.productService.deleteProduct(id).subscribe({
next: () => {
console.log('Product deleted');
this.loadProducts();
},
error: (error) => {
console.error('DELETE Error:', error);
}
});
}
}
5. app.html
<h1>Angular HttpClient CRUD</h1>
<button (click)="addProduct()">
Add Product
</button>
<hr>
<table border="1">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Price</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@for (product of products; track product.id) {
<tr>
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td>{{ product.price }}</td>
<td>
<button (click)="updateProduct(product.id)">
Update
</button>
<button (click)="deleteProduct(product.id)">
Delete
</button>
</td>
</tr>
}
</tbody>
</table>
6. app.css
table {
border-collapse: collapse;
margin-top: 20px;
}
th,
td {
padding: 10px;
}
button {
margin: 5px;
padding: 8px 12px;
}
7. main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { App } from './app/app';
import { appConfig } from './app/app.config';
bootstrapApplication(App, appConfig)
.catch((error) => console.error(error));
How it works
The main flow is:
Component
↓
ProductService
↓
HttpClient
↓
REST API
The component should generally not contain the API URL or directly repeat HTTP logic. The reusable HTTP operations are placed in ProductService.
GET
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(this.apiUrl);
}
Request:
GET https://localhost:7001/api/products
The component subscribes:
this.productService.getProducts().subscribe({
next: (data) => {
this.products = data;
},
error: (error) => {
console.error(error);
}
});
POST
createProduct(product: Product): Observable<Product> {
return this.http.post<Product>(
this.apiUrl,
product
);
}
Request:
POST https://localhost:7001/api/products
Angular automatically serializes the object into JSON:
{
"id": 0,
"name": "Laptop",
"price": 75000
}
PUT
updateProduct(id: number, product: Product): Observable<void> {
return this.http.put<void>(
`${this.apiUrl}/${id}`,
product
);
}
For:
updateProduct(10, product)
the request becomes:
PUT https://localhost:7001/api/products/10
with the product object sent in the request body.
DELETE
deleteProduct(id: number): Observable<void> {
return this.http.delete<void>(
`${this.apiUrl}/${id}`
);
}
For:
deleteProduct(10)
Angular sends:
DELETE https://localhost:7001/api/products/10
Most important syntax
// GET
this.http.get<Product[]>(url);
// GET by ID
this.http.get<Product>(`${url}/${id}`);
// POST
this.http.post<Product>(url, product);
// PUT
this.http.put<void>(`${url}/${id}`, product);
// DELETE
this.http.delete<void>(`${url}/${id}`);
And for standalone Angular, don't forget:
provideHttpClient()
in app.config.ts.