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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@profusion/ngx-fetcher-with-etag

v0.0.2

Published

**A smart, ETag-powered data fetching library for modern Angular applications.**

Readme

ngx-fetcher-with-etag

A smart, ETag-powered data fetching library for modern Angular applications.

Stop re-fetching unchanged data. ngx-fetcher-with-etag simplifies your data layer with automatic caching, smart polling, and utilities for handling local data.


🤔 Why ngx-fetcher-with-etag?

Modern web apps often make redundant API calls, fetching the same data repeatedly. This library solves that problem by leveraging browser caching and HTTP ETag headers. The server tells you when data hasn't changed, saving bandwidth and making your app feel faster.

This library automates that entire process and packages it into a clean, reactive, RxJS-powered API.


✨ Features

  • ETag: Seamlessly uses ETag / If-None-Match headers to avoid re-downloading unchanged data.
  • 🔄 Polling: Automatically refreshes data based on server Expires headers or custom intervals.
  • 🔍 Local Lookups: A service for fetching a list and then accessing individual items synchronously by ID.
  • 🛡️ Extensible Authentication: A simple AuthProvider system to inject authentication headers into HTTP requests.
  • 👁️ Fully Reactive: Built from the ground up with RxJS Observables.

🚀 Installation

npm install ngx-fetcher-with-etag

Core Concepts

The library is built around a few key components that work together:

  1. FetcherWithEtag: The core engine. It handles a single API endpoint, managing ETag caching and polling.
  2. LocalDataLoaderEtagService: The most advanced utility. It uses FetcherWithEtag to fetch an array of items and then indexes them for instant, synchronous access by ID.
  3. AuthProvider: A simple class you extend to provide authentication headers for your HTTP requests.

📖 API and Usage

FetcherWithEtag

This is the main class for fetching data from a single endpoint. It handles all the ETag, caching, and polling logic for you.

Basic Usage

import { FetcherWithEtag } from 'ngx-fetcher-with-etag';

// Create a new fetcher instance
const fetcher = new FetcherWithEtag(
  httpClient,
  authProvider,
  'https://api.example.com/data',
  (raw: RawData) => new Data(raw), // Converter function
  (old: Data, new: Data) => old.id === new.id // Equality check
);

// Subscribe to data updates (only emits when data has actually changed)
fetcher.data$.subscribe(data => console.log('Data updated:', data));

// Subscribe to the loading state
fetcher.loading$.subscribe(loading => console.log('Is loading:', loading));

Constructor Parameters Explained

Converter Function (raw: RawT) => T

This function's job is to transform the raw, plain JSON response from your API into a new type or interface that your application can use. This is the perfect place to map fields, add computed properties, or parse dates.

// Example: Convert a raw API user into a clean User model
type RawUser = { user_id: string; full_name: string; };
type User = {
  id: string;
  name: string;
  initial: string;
};

function userConverter(raw: RawUser): User {
  return {
    id: raw.user_id,
    name: raw.full_name,
    initial: raw.full_name.charAt(0)
  };
}

Equality Check Function (old: T, new: T) => boolean

This function prevents your data$ observable from emitting a new value if the data is effectively the same. This is crucial for performance and preventing unnecessary re-renders in your UI. You can define what "equal" means—whether it's a simple ID check or a deep object comparison.

// Only emit if the user's name has changed
function userEqualityCheck(old: User, new: User): boolean {
  return old.name === new.name;
}

// Only emit if something has changed
type ItemWithEtag = {
  id: string;
  etag: string;
};

function userEtagCheck(old: ItemWithEtag, new: ItemWithEtag): boolean {
  return old.etag === new.etag;
}

LocalDataLoaderEtagService

This is a powerful abstract service for a common pattern: fetching a list of items and then needing to look up individual items from that list by their ID. It combines the caching of FetcherWithEtag with an in-memory, indexed store for O(1) lookups.

When to use it

Use this when you have a users or products endpoint and your app frequently needs to ask, "What's the name of the user with ID user-123?" without making another API call.

Example Implementation

import { LocalDataLoaderEtagService } from 'ngx-fetcher-with-etag';

// 1. Define your data structures
type User = { id: string; name: string; email: string; };
type RawUsersResponse = {
  etag: string; // ETag is required for comparison
  items: User[];
};
type ProcessedUsersData = RawUsersResponse & {
  // This will be added automatically: a map for fast lookups
  byId: Record<string, User>;
};

// 2. Create a service that extends the base class
@Injectable({ providedIn: 'root' })
export class UsersService extends LocalDataLoaderEtagService<
  'items',         // The key in the response containing the array
  'id',            // The key on each item to use as its ID
  User,            // The type of a single item
  ProcessedUsersData, // The final processed data structure (with `byId`)
  RawUsersResponse    // The raw API response
> {
  constructor(http: HttpClient, auth: AuthProvider) {
    super(
      // Provide a function that creates the underlying FetcherWithEtag
      (converter, isEqual) => new FetcherWithEtag(
        http,
        auth,
        'https://api.example.com/users',
        converter,
        isEqual
      ),
      'id',    // Tell it the ID field is named 'id'
      'items'  // Tell it the array is in the 'items' field
    );
  }
}

// 3. Use the service in your component
constructor(private usersService: UsersService) {}

this.usersService.dataLoader$.subscribe((dataLoader) => {
  // dataLoader is an object with a `load` method for synchronous access
  const user = dataLoader.load('user-123'); // Instant local lookup!
  if (user) {
    console.log('User found locally:', user.name);
  }
});

Authentication

Provide authentication headers to your requests by creating a simple AuthProvider.

Example: JWT Authentication

import { AuthProvider } from 'ngx-fetcher-with-etag';
import { HttpHeaders } from '@angular/common/http';

@Injectable({ providedIn: 'root' })
export class JwtAuthProvider extends AuthProvider {
  override getAuthHeaders(): HttpHeaders {
    const token = localStorage.getItem('jwt_token');
    if (token) {
      return new HttpHeaders({
        Authorization: `Bearer ${token}`
      });
    }
    return new HttpHeaders();
  }
}