npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

angular-fetcher

v20.1.1

Published

Signal-based state management for remote API data in Angular with support for reactive loading, error tracking, optimistic updates, and auto refetching.

Downloads

108

Readme

Angular Fetcher

Signal-Based State Management for Remote API Data

angular-fetcher is a modern, signal-based library for managing remote API data in Angular applications. It offers a clean, reactive way to handle data fetching, mutations, and error tracking, all while keeping your code organized and type-safe. Built to work seamlessly with Angular's ecosystem, it empowers you to create responsive, robust apps with minimal effort. for managing remote API data in Angular applications. It offers a clean, reactive way to handle data fetching, mutations, and error tracking, all while keeping your code organized and type-safe. Built to work seamlessly with Angular's ecosystem, it empowers you to create responsive, robust apps with minimal effort.

Table of Contents

Why Angular Fetcher?

angular-fetcher makes working with remote APIs in Angular a breeze. By leveraging Angular Signals and RxJS, it provides a powerful, type-safe API to manage data, loading states, and errors reactively. Whether you're fetching user lists or updating records with optimistic updates, this library ensures your app stays performant and maintainable, all with a syntax that feels natural in Angular.

Features

  • Reactive Signals: Track data, loading states, and errors in real-time using Angular Signals.
  • Data Fetching: Load or refresh API data with full control over existing state.
  • Mutations: Perform updates with optimistic changes and track specific mutation states using mutationLoadingKey.
  • Type Safety: Enjoy TypeScript support for better code reliability and IDE assistance.
  • Angular Integration: Works smoothly with Angular's dependency injection and RxJS.

Angular Compatibility

| Angular Version | Supported Package Version | | --------------- | ------------------------- | | Angular 20.x | ^20.1.1 | | Angular 19.x | ^19.0.2 | | Angular 18.x | ^18.0.1 | | Angular 17.x | ^17.0.1 |

Please install the latest patch and minor version of each major release to ensure optimal compatibility.

Installation

Add angular-fetcher to your project:

npm install angular-fetcher

Fetching Data

Create a resource in your service to fetch data from an API using withResource. This sets up a reactive state for data, loading, and errors.

import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { withResource } from "angular-fetcher";
import { Observable } from "rxjs";

interface User {
  id: number;
  name: string;
}

@Injectable({ providedIn: "root" })
export class UserService {
  private http = inject(HttpClient);

  usersResource = withResource(() => this.getUsers());

  private getUsers(): Observable<User[]> {
    return this.http.get<User[]>("/api/users");
  }
}

Use the resource in a component to display data and handle refreshes. The invalidate() method clears the current data and fetches fresh results.

import { Component } from "@angular/core";
import { UserService } from "./user.service";

@Component({
  selector: "app-user-list",
  template: `
    <button (click)="reload()">Reload Users</button>

    @if (users.state.fetchLoading()) {
    <div>Loading users...</div>
    } @else if (users.state.error()) {
    <div class="error">{{ users.state.error()?.message }}</div>
    } @else {
    <div class="user-list">
      @for (user of users.state.data(); track user.id) {
      <div>{{ user.name }}</div>
      }
    </div>
    }
  `,
  styles: [
    `
      .user-list {
        margin-top: 16px;
      }
      .error {
        color: red;
      }
    `,
  ],
})
export class UserListComponent {
  userService = inject(UserService);
  users = this.userService.usersResource;

  ngOnInit() {
    this.users.fetch(); // Load data initially
  }

  reload() {
    this.users.invalidate(); // Clear and refetch data
  }
}

You can also use the optional handlers argument with fetch() to respond to success or error states programmatically:

this.users.fetch({
  next: (res) => this.showToast("Users loaded: ", res),
  error: (err) => this.showToast(`Failed to load users: ${err.message}`),
});

Handling Mutations

Use withMutation to perform operations like adding a user, with support for optimistic updates and tracking specific mutation states via mutationLoadingKey.

import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { withResource } from "angular-fetcher";
import { Observable } from "rxjs";

interface User {
  id: number;
  name: string;
}

@Injectable({ providedIn: "root" })
export class UserService {
  private http = inject(HttpClient);

  usersResource = withResource(() => this.getUsers());

  private getUsers(): Observable<User[]> {
    return this.http.get<User[]>("/api/users");
  }

  addUser(user: User): Observable<User> {
    return this.http.post<User>("/api/users", user);
  }
}

In a component, trigger the mutation and show a success message using a showToast method.

import { Component } from "@angular/core";
import { UserService } from "./user.service";

@Component({
  selector: "app-add-user",
  template: `
    <input [(ngModel)]="newUser.name" placeholder="Enter name" />
    <button
      (click)="addUser()"
      [disabled]="users.state.mutationLoadingKey()['add-user']"
    >
      {{
        users.state.mutationLoadingKey()["add-user"] ? "Adding..." : "Add User"
      }}
    </button>
  `,
})
export class AddUserComponent {
  userService = inject(UserService);
  users = this.userService.usersResource;
  newUser: User = { id: 0, name: "" };

  addUser() {
    this.users.withMutation(this.userService.addUser(this.newUser), {
      key: "add-user",
      optimisticUpdate: (prev) => [...prev, this.newUser],
      invalidate: true, // Refetch users after mutation
      next: () => this.showToast("User added successfully!"),
      error: (err) => this.showToast(`Error: ${err.message}`),
    });
    this.newUser = { id: 0, name: "" };
  }

  private showToast(message: string) {
    console.log(message); // Replace with your toast service
  }
}

Optimistic Update Explanation: Optimistic update is a technique where changes are applied immediately in the UI before receiving confirmation from the server. This improves perceived responsiveness by instantly reflecting user actions. If the server confirms the change, the update remains; if the request fails, the UI reverts to the previous state to maintain consistency.

The mutationLoadingKey()['add-user'] tracks the mutation's loading state, and optimisticUpdate instantly updates the UI while invalidate: true ensures the data syncs with the server.

Empty Value Option

You can set an emptyValue in withResource to define the initial or reset state of the data. For example:

usersResource = withResource(() => this.getUsers(), {
  emptyValue: [] as User[],
});

This ensures the data starts as an empty array until fetched.

Invalidate vs. Fetch

  • fetch(): Loads data while keeping the current data until the new response arrives. Perfect for initial loads or refreshing without clearing the UI.
  • invalidate(): Resets the data to its emptyValue (or empty object if not set) and fetches fresh results. Ideal for ensuring the latest server state, like after a mutation.

Abort Requests

By default, if multiple requests are made to the same resource and a previous request has not yet completed, Angular Fetcher will automatically abort the previous request and only proceed with the latest one. This ensures your data is always fresh and consistent with the most recent interaction.

You can also abort a request manually using the abort() method if it hasn't completed yet:

this.usersResource.abort();

This is useful for cancelling background requests when navigating away from a view, or to reset state deliberately.

Http Interceptor

Because angular-fetcher uses Angular’s HttpClient internally, it fully supports all features of Angular’s HTTP client, including interceptors. This means you can take advantage of authentication tokens, logging, error handling, and any custom request modifications seamlessly within angular-fetcher without additional setup.

License

MIT License | Ali Montazeri