# Exercise: an Angular Client for a Web API

Give your products API a front end. A service calls the API with HttpClient; a component lists the products, filters them by category, deletes them and adds new ones, and says so when something fails. Ten tests answer for a pretend API, so they need nothing running.

Journeyman · Angular · REST API · Updated October 30, 2026

In *Exercise: Northwind Web API* you built an API for Northwind's products. Now give it a front end: an Angular page that lists the products, filters them by category, deletes them and adds new ones.

## Get the exercise code

Open this lesson's GitHub repository, [github.com/ask-codey/exercise-an-angular-client-for-a-web-api](https://github.com/ask-codey/exercise-an-angular-client-for-a-web-api), also linked from the **Exercise** box beside the lesson. Click **Code**, then **Download ZIP**, and unzip it into your `bootcamp` folder. Or clone it with Git. Open the folder in **VS Code**, and a terminal there:

```bash
npm install
npm test
```

The tests fail to start with.

| File | What's in it |
|---|---|
| `src/app/product.ts` | The `Product` interface, shaped like the API's JSON, and Northwind's eight categories. Finished. |
| `src/app/product-api.ts` | The service that calls the API, for you to finish |
| `src/app/product-list/product-list.ts` | The products page's class, for you to finish. Its requirements are in the comment at the top. |
| `src/app/product-list/product-list.html` | Its template, for you to finish |
| `src/app/client.spec.ts` | Ten tests |
| `proxy.conf.json` | Sends the app's `/api` requests to http://localhost:5179 while you develop |

The tests don't need your API running. They use Angular's **`HttpTestingController`**: it catches each request the app sends, and the test answers it, with products or with an error. So a test can check, for example, that choosing *Condiments* sends `GET /api/products?categoryId=2`, then answer with one product and check that the table shows just that one.

## What to do

**1. The service.** Finish `ProductApi`:

- `getAll()` sends `GET /api/products`; `getAll(2)` sends `GET /api/products?categoryId=2`;
- `add(product)` sends `POST /api/products` with the product, and gives back the product the API sends back, with its new id;
- `delete(7)` sends `DELETE /api/products/7`.

Each returns the observable from `HttpClient`. The component subscribes.

**2. The page.** Finish `ProductList`:

- while the products load, *Loading products...*; then a table, one row per product, with a **Delete** button;
- if loading fails, *Sorry, the products couldn't be loaded (500).*, with the real status;
- a **Category** list: choosing one loads that category's products, and *All categories* loads them all again;
- **Delete** removes the product with the API, and its row when the API answers;
- an **Add a product** form: name, price, category and an **Add** button. It adds the product the API sends back to the end of the table, and empties the form. Without a name, it says *Give the product a name.* and sends nothing.

The exact ids and classes the tests look for are in the comment at the top of `product-list.ts`.

## Hints

- **The service:** `inject(HttpClient)`, then `this.http.get<Product[]>(url)`, `post<Product>(url, product)` and `delete<void>(url)`. Build the URL with a template string.
- **The state:** signals for the products, *is it loading?*, the message, and the chosen category. `subscribe({ next, error })` sets them.
- **The status:** the error is an `HttpErrorResponse`, and its `status` is the number for the message.
- **The category list:** `[ngModel]="categoryId()" (ngModelChange)="chooseCategory($event)"` shows the choice, and calls your method with the new one. The value is a string: `''` for all, or `'2'`, so turn it into a number before calling the service.
- **After a delete or an add**, change the list signal with `update`, using `filter` or `[...products, added]`, instead of loading everything again.
- **The form:** `[(ngModel)]` on each box, with `FormsModule` in the component's `imports`. Money: `{{ product.unitPrice | currency }}`, with `CurrencyPipe` in the imports.

## Try it for real

When the tests pass, start your API from *Exercise: Northwind Web API* (**Ctrl+F5**), and this app with `ng serve`. If your API isn't at port 5179, change the port in `proxy.conf.json` first. Open http://localhost:4200:

1. All 77 products load. Choose *Seafood*, and there are 12.
2. Add *Trail Mix* at 4.50 in *Confections*. It appears at the end of the table, and the form empties.
3. Reload the page: Trail Mix is still there, because it's in the database. Delete it.
4. Stop the API and choose another category. The message says the products couldn't be loaded, with the status `502`, *Bad Gateway*: the dev server's proxy answered for the API it couldn't reach.

## Submit your exercise

The exercise is how you finish this lesson. When your work is done, put it on GitHub in a repo of your own, then submit that repo's address here.

1. On GitHub, click **+**, then **New repository**. Name it (for example `exercise-an-angular-client-for-a-web-api`), choose **Public**, and click **Create repository**. Leave out the README: your folder already has one.
2. In a terminal in your exercise folder (Git Bash on Windows), send your work to your new repo. If you downloaded the ZIP:

   ```bash
   git init -b main
   git add .
   git commit -m "My solution"
   git remote add origin https://github.com/your-name/exercise-an-angular-client-for-a-web-api.git
   git push -u origin main
   ```

   If you cloned the starter instead, point your copy at your own repo, then commit and push:

   ```bash
   git remote set-url origin https://github.com/your-name/exercise-an-angular-client-for-a-web-api.git
   git add .
   git commit -m "My solution"
   git push -u origin main
   ```

3. Paste your repo's address, such as `https://github.com/your-name/exercise-an-angular-client-for-a-web-api`, into **Submit your exercise** on this page. The site checks that the repo is public and holds your commits, and then the lesson is complete.

Changed something after you submitted? Push it, and submit again: your latest submission is the one that counts.

## So what

A service that only talks HTTP, a component that only shows signals, and tests that stand in for the server. That's the shape of every Angular page that reads and writes data, and the routing chapter next builds on it.

## Key terms

- [HttpClient](https://www.ask-codey.com/glossary/httpclient-c-sharp)
- Service
- Observable
- subscribe()

## Related

- [HttpClient in Angular](https://www.ask-codey.com/lessons/calling-apis-from-angular-httpclient-in-angular)
- [Exercise: Northwind Web API](https://www.ask-codey.com/lessons/exercise-northwind-web-api)

Source: https://www.ask-codey.com/lessons/exercise-an-angular-client-for-a-web-api
