Angular HttpClient: Get Data from an API with a Service

Angular Tutorial: Beginner to Advanced · 10. HTTP and RxJS · 3 min read · Updated

Most apps load data from a server. Angular's HttpClient sends HTTP requests and returns Observables, a stream type you will study in the next lessons. Here you load a list of posts from a public test API.

What you will learn

  • How to enable HttpClient with provideHttpClient()
  • How to write a typed data service
  • How to show the data with toSignal

Enable HttpClient

// src/app/app.config.ts
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { provideHttpClient, withFetch } from '@angular/common/http';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideRouter(routes, withComponentInputBinding()),
    provideHttpClient(withFetch()),
  ],
};

withFetch() makes HttpClient use the browser's modern fetch API.

A typed service

// src/app/posts/post.ts
export interface Post {
  id: number;
  userId: number;
  title: string;
  body: string;
}
// src/app/posts/post-api.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Post } from './post';

@Injectable({ providedIn: 'root' })
export class PostApi {
  private readonly http = inject(HttpClient);
  private readonly baseUrl = 'https://jsonplaceholder.typicode.com/posts';

  getPosts(): Observable<Post[]> {
    return this.http.get<Post[]>(this.baseUrl);
  }

  getPost(id: number): Observable<Post> {
    return this.http.get<Post>(`${this.baseUrl}/${id}`);
  }
}

http.get<Post[]>() tells TypeScript the shape of the response. It does not check the data at run time, so it describes what you expect the server to send.

Show the posts

// src/app/posts/post-list.ts
import { Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { PostApi } from './post-api';

@Component({
  selector: 'app-post-list',
  template: `
    <h2>Posts</h2>
    @if (posts(); as list) {
      <ul>
        @for (post of list; track post.id) {
          <li>
            <strong>{{ post.title }}</strong>
          </li>
        }
      </ul>
    } @else {
      <p>Loading posts...</p>
    }
  `,
})
export class PostList {
  private readonly api = inject(PostApi);

  // Subscribes for you and unsubscribes when the component is destroyed.
  protected readonly posts = toSignal(this.api.getPosts());
}
// src/app/app.ts
import { Component } from '@angular/core';
import { PostList } from './posts/post-list';

@Component({
  imports: [PostList],
  selector: 'app-root',
  templateUrl: './app.html',
  styleUrl: './app.css',
})
export class App {}
<!-- src/app/app.html -->
<main>
  <h1>Latest posts</h1>
  <app-post-list />
</main>

To keep the routing demo, you can add a posts route to app.routes.ts instead of replacing app.html. Either way, run ng serve and the posts appear after a moment.

The key parts

  • An Observable does nothing until something subscribes to it. toSignal subscribes for you.
  • toSignal returns undefined until the first value arrives, which is why the template shows "Loading..." in the @else branch.
  • Put HTTP code in a service, not in components, so it can be reused and tested.
  • HttpClient automatically parses JSON.

Try it yourself

  1. Show the first 100 characters of each post body.
  2. Add a post detail page that uses getPost(id) and a route parameter.
  3. Open the Network tab and find the request.

Quick check

  1. Which function enables HttpClient?
  2. When does an Observable send its request?
  3. Why put HTTP calls in a service?

Real requests are slow and sometimes fail. Next, handle loading and errors, and send data with POST. See the HttpClient guide for more.

Frequently asked questions

Do I still need to unsubscribe?

Not with toSignal or the async pipe. If you call subscribe() yourself, clean up with takeUntilDestroyed().

Why do I get a CORS error?

The browser blocks calls to another origin unless the server allows it. Fix it on the server by sending the right CORS headers, not in Angular.