Skip to main content
Angular Basics
CHAPTER 27 Beginner

Building REST API Applications

Updated: May 18, 2026
5 min read

# CHAPTER 27

Building REST API Applications

1. Chapter Introduction

This chapter brings together everything learned so far — Services, HttpClient, Reactive Forms, Routing, Guards — into a complete, production-style Blog Management Application. We will implement full CRUD (Create, Read, Update, Delete) operations against a REST API, following industry-standard Angular architecture patterns.

2. Application Architecture

text
12345678910111213
Blog App Architecture:

[ Routes ]
  /blogs         → BlogListComponent (List all posts)
  /blogs/new     → BlogFormComponent (Create new post)
  /blogs/:id     → BlogDetailComponent (View single post)
  /blogs/:id/edit → BlogFormComponent (Edit existing post)

[ Services ]
  BlogService — All HTTP calls (get, create, update, delete)

[ Models ]
  Blog interface (id, title, content, author, createdAt, published)

3. Blog Interface

typescript
123456789
// models/blog.model.ts
export interface Blog {
  id?: number;
  title: string;
  content: string;
  author: string;
  createdAt?: string;
  published: boolean;
}

4. Blog Service (Complete CRUD)

blog.service.ts
1234567891011121314151617181920212223242526272829303132333435363738
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, catchError, throwError } from 'rxjs';
import { Blog } from './models/blog.model';

@Injectable({ providedIn: 'root' })
export class BlogService {
  private apiUrl = 'https://jsonplaceholder.typicode.com/posts'; // Demo API

  constructor(private http: HttpClient) {}

  getAll(): Observable<Blog[]> {
    return this.http.get<Blog[]>(this.apiUrl).pipe(catchError(this.handleError));
  }

  getById(id: number): Observable<Blog> {
    return this.http.get<Blog>(`${this.apiUrl}/${id}`).pipe(catchError(this.handleError));
  }

  create(blog: Blog): Observable<Blog> {
    return this.http.post<Blog>(this.apiUrl, blog).pipe(catchError(this.handleError));
  }

  update(id: number, blog: Blog): Observable<Blog> {
    return this.http.put<Blog>(`${this.apiUrl}/${id}`, blog).pipe(catchError(this.handleError));
  }

  delete(id: number): Observable<void> {
    return this.http.delete<void>(`${this.apiUrl}/${id}`).pipe(catchError(this.handleError));
  }

  private handleError(error: HttpErrorResponse): Observable<never> {
    let message = &#039;An unknown error occurred&#039;;
    if (error.status === 404) message = &#039;Blog post not found&#039;;
    if (error.status === 500) message = &#039;Server error. Please try again later&#039;;
    return throwError(() => new Error(message));
  }
}

5. Blog List Component

blog-list.component.ts
1234567891011121314151617181920212223
@Component({ selector: &#039;app-blog-list&#039;, templateUrl: &#039;./blog-list.component.html&#039; })
export class BlogListComponent implements OnInit {
  blogs: Blog[] = [];
  loading = true;
  error = &#039;&#039;;

  constructor(private blogService: BlogService, private router: Router) {}

  ngOnInit(): void {
    this.blogService.getAll().subscribe({
      next: blogs => { this.blogs = blogs.slice(0, 10); this.loading = false; },
      error: err => { this.error = err.message; this.loading = false; }
    });
  }

  deleteBlog(id: number): void {
    if (confirm(&#039;Are you sure you want to delete this post?&#039;)) {
      this.blogService.delete(id).subscribe(() => {
        this.blogs = this.blogs.filter(b => b.id !== id);
      });
    }
  }
}
html
1234567891011121314151617181920
<!-- blog-list.component.html -->
<div class="header">
  <h1>Blog Management</h1>
  <a routerLink="/blogs/new" class="btn-primary">+ New Post</a>
</div>

<div *ngIf="loading" class="loading">Loading posts...</div>
<div *ngIf="error" class="error">{{ error }}</div>

<div class="blog-grid">
  <div class="blog-card" *ngFor="let blog of blogs">
    <h3>{{ blog.title | titlecase }}</h3>
    <p>{{ blog.body | slice:0:100 }}...</p>
    <div class="actions">
      <a [routerLink]="[&#039;/blogs', blog.id]">View</a>
      <a [routerLink]="[&#039;/blogs', blog.id, 'edit']">Edit</a>
      <button (click)="deleteBlog(blog.id!)">Delete</button>
    </div>
  </div>
</div>

6. Blog Form Component (Create & Edit)

blog-form.component.ts
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
@Component({ selector: &#039;app-blog-form&#039;, templateUrl: &#039;./blog-form.component.html&#039; })
export class BlogFormComponent implements OnInit {
  blogForm!: FormGroup;
  isEditMode = false;
  blogId!: number;
  loading = false;
  success = &#039;&#039;;

  constructor(
    private fb: FormBuilder,
    private blogService: BlogService,
    private route: ActivatedRoute,
    private router: Router
  ) {}

  ngOnInit(): void {
    this.blogForm = this.fb.group({
      title: [&#039;&#039;, [Validators.required, Validators.minLength(5)]],
      body: [&#039;&#039;, [Validators.required, Validators.minLength(20)]],
      userId: [1]
    });

    const id = this.route.snapshot.paramMap.get(&#039;id&#039;);
    if (id) {
      this.isEditMode = true;
      this.blogId = +id;
      this.blogService.getById(this.blogId).subscribe(blog => {
        this.blogForm.patchValue(blog);
      });
    }
  }

  onSubmit(): void {
    if (this.blogForm.invalid) return;
    this.loading = true;

    const request$ = this.isEditMode
      ? this.blogService.update(this.blogId, this.blogForm.value)
      : this.blogService.create(this.blogForm.value);

    request$.subscribe({
      next: () => this.router.navigate([&#039;/blogs&#039;]),
      error: () => { this.loading = false; }
    });
  }
}

7. API Best Practices

  • Always type your API responses: Use TypeScript interfaces to catch mismatches at compile time.
  • Use services, not components, for HTTP: Components should never contain this.http.get() directly.
  • Handle all 3 states: Loading, Success, and Error. Never leave the user staring at a blank screen.
  • Centralize error handling: Use an HTTP Interceptor for global 401/500 error handling.

8. MCQs with Answers

Question 1

In a clean architecture, which layer should contain all HTTP calls?

Question 2

What HTTP method is used to fully update an existing resource?

Question 3

What HTTP method partially updates a resource (only specified fields)?

Question 4

What does patchValue() do on a FormGroup?

Question 5

What is the pattern for handling Create and Edit in one form component?

Question 6

What operator converts a DTO (API response) to a different format?

Question 7

What is the REST endpoint convention for deleting resource with id 5?

Question 8

What property makes a button unclickable while a form is submitting?

Question 9

What method of FormGroup sets ALL values at once (requires all fields)?

Question 10

What is the benefit of defining a TypeScript interface for API responses?

9. Interview Questions

  • Q: How would you architect a full CRUD Angular application following the separation of concerns principle?
  • Q: What is the difference between setValue() and patchValue() in Angular Reactive Forms?

10. Summary

Building REST API applications is the core skill of a frontend developer. By applying the Service layer for HTTP, Reactive Forms for data input, Routing for navigation, and TypeScript interfaces for type safety, we build maintainable, scalable, and testable Angular applications that professional teams can confidently deploy to production.

11. Next Chapter Recommendation

In Chapter 28: Angular Interview Preparation, we review the top 50 Angular interview questions spanning components, RxJS, performance, and architecture — so you can walk into any Angular interview with confidence.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·