This article walks through the process of using the stable @tanstack/query-angular package in Angular v18 applications to handle API requests more effectively 🚀
Handling API calls in Angular projects typically involves writing a lot of repeated code, which can lead to duplication, higher maintenance costs, and tricky state synchronization. Although @tanstack/angular-query-experimental offers a feature-rich option, it remains in an experimental phase and should be used with caution. For Angular v17 and v18 projects, the established @tanstack/query-angular package is a more dependable choice.
Core Advantages of TanStack Angular Query:
Declarative API Definition: Create query functions that encapsulate both the request and any data transformation logic, resulting in cleaner, more maintainable code.
Automatic Caching & Refetching: Take advantage of built-in caching models such as "stale-while-revalidate" to boost performance and minimize redundant network traffic. You can trigger refetches based on various conditions, like data becoming stale or the window regaining focus, to keep information current.
Reactive Data Handling: Manage data using observables and signals, which allows UI components to update reactively without needing manual lifecycle management or complex state solutions.
Modular Structure: Package API logic into reusable query units to improve code organization and ease of maintenance.
Specialized Devtools: Use the TanStack Query Devtools to monitor query activity, cache state, and refetch behavior, making debugging and performance tuning more straightforward.
How to Install:
Use npm or yarn to install the required packages:
npm install @tanstack/query-angular-experimental @tanstack/angular-query-devtools-experimental
Detailed Implementation Steps:
Even though @tanstack/angular-query-experimental exists, it's still in development and isn't suitable for production environments. For dependable API call management in Angular projects, the stable @tanstack/query-angular package is the recommended path.
Here's a guide to implementing it step by step:
1. Configure Providers in app.config.ts:
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';
import { QueryClient, provideAngularQuery } from '@tanstack/angular-query-experimental';
const queryClient = new QueryClient();
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(),
provideAngularQuery(queryClient)
]
};
- Bring in
provideHttpClient,QueryClient, andprovideAngularQuery. - Create and export the app configuration object by setting up routing, HTTP client, and Angular Query through dependency injection.
2. Create a TypeScript Type for the API Response:
TypeScript types outline the expected shape of your data, which helps for better code structure and preventing bugs. In your app, this type acts as a contract for the data coming back from an API call or another data source.
export type Response = {
name: string
description: string
subscribers_count: number
stargazers_count: number
forks_count: number
}
3. Build a Service (repos.service.ts) to Handle the API Call:
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Response } from '../../types/responce.type';
@Injectable({
providedIn: 'root'
})
export class ReposService {
endpoint: string = 'https://api.github.com';
constructor(
private http: HttpClient
) { }
getRepos() {
return this.http.get<Response>(`${this.endpoint}/repos/tanstack/query`);
}
}
The service's job is to contact the GitHub API for repository information. It leverages HttpClient for the request and expects the response to match the Response type defined earlier.
4. Set Up Angular Query in the Component (github-repo-list.component.ts):
The component relies on Angular Query for data fetching and caching. It takes an instance of ReposService to perform the actual HTTP request. A query is defined with the unique key 'repoData' that utilizes a queryFn to retrieve the repository list.
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { injectQuery } from '@tanstack/angular-query-experimental';
import { AngularQueryDevtools } from '@tanstack/angular-query-devtools-experimental';
import { lastValueFrom } from 'rxjs';
import { ReposService } from '../services/repos/repos.service';
@Component({
selector: 'app-github-repo-list',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [AngularQueryDevtools],
templateUrl: './github-repo-list.component.html',
styleUrl: './github-repo-list.component.scss'
})
export class GithubRepoListComponent{
reposService = inject(ReposService);
query = injectQuery(() => ({
queryKey: ['repoData'],
queryFn: () =>
lastValueFrom(
this.reposService.getRepos()
),
}))
}
5. Display the Data in the Component Template (github-repo-list.component.html):
@if (query.isPending()) {
Loading...
}
@if (query.error()) {
An error has occurred: {{ query.error()?.message }}
}
@if (query.data(); as data) {
<h1>Name: {{ data.name }}</h1>
<p>Description: {{ data.description }}</p>
<strong>👀 {{ data.subscribers_count }}</strong>
<strong>✨ {{ data.stargazers_count }}</strong>
<strong>🍴 {{ data.forks_count }}</strong>
}
<angular-query-devtools initialIsOpen />
- The markup uses conditionals to show different content based on the query's status—whether it's still loading, encountered an error, or was successful.
- It gracefully manages each state, offering the user a clear indication of what's happening.
- It also includes Angular Query devtools to make inspection and debugging easier.
See It in Action:
You can find the full source code in this GitHub repository. Thanks 😊

