Spoosh
Injects

injectRead

Fetch data with automatic caching and triggering using Signals

Basic Usage

@Component({
  selector: "app-user-list",
  template: `
    @if (loading()) {
      <div>Loading...</div>
    } @else if (error()) {
      <div>Error: {{ error()?.message }}</div>
    } @else {
      <ul>
        @for (user of data(); track user.id) {
          <li>{{ user.name }}</li>
        }
      </ul>
    }
  `,
})
export class UserListComponent {
  private users = injectRead((api) => api("users").GET());

  data = this.users.data;
  loading = this.users.loading;
  error = this.users.error;
}

With Query Parameters

@Component({
  selector: "app-user-list",
  template: `...`,
})
export class UserListComponent {
  private isReady = signal(false);

  private users = injectRead(
    (api) => api("users").GET({ query: { page: 1, limit: 10 } }),
    {
      enabled: this.isReady,
      tags: ["users", "dashboard"],
    }
  );

  data = this.users.data;
  input = this.users.input;
}

Custom Tags

By default, a tag is auto-generated from the resolved path. You can override with custom tags:

// Auto-generated tag (default)
injectRead((api) => api("users/:id/posts").GET({ params: { id: "123" } }));
// → tag: "users/123/posts"

// Single custom tag
injectRead((api) => api("users/:id/posts").GET({ params: { id: "123" } }), {
  tags: "user-posts",
});
// → tag: "user-posts"

// Multiple custom tags
injectRead((api) => api("posts").GET(), {
  tags: ["posts", "dashboard"],
});
// → tags: ["posts", "dashboard"]

Options

OptionTypeDefaultDescription
enabledboolean | Signal<bool>trueWhether to fetch automatically
tagsstring | string[]-Custom tags (overrides auto-generated tag)
+ plugin options--Options from installed plugins

Returns

PropertyTypeDescription
dataSignal<TData | undefined>Response data signal
errorSignal<TError | undefined>Error signal if request failed
loadingSignal<boolean>Signal true during initial load
fetchingSignal<boolean>Signal true during any fetch
trigger(options?) => PromiseManually trigger fetch with optional overrides
abort() => voidAbort current request
inputSignal<object>The request input (query, body, params)
metaSignal<object>Plugin-provided metadata

Trigger with Override Options

The trigger function accepts optional parameters to override the request:

@Component({
  selector: "app-user-detail",
  template: `
    <button (click)="loadUser('1')">Load User 1</button>
    <button (click)="loadUser('2')">Load User 2</button>
    <button (click)="refresh()">Refresh</button>
    @if (data()) {
      <div>{{ data()?.name }}</div>
    }
  `,
})
export class UserDetailComponent {
  private user = injectRead((api) =>
    api("users/:id").GET({ params: { id: "1" } })
  );

  data = this.user.data;
  trigger = this.user.trigger;

  loadUser(id: string) {
    // Fetch with different params
    this.trigger({ params: { id } });
  }

  refresh() {
    // Force refetch (bypass cache)
    this.trigger({ force: true });
  }
}

You should avoid using custom options with trigger as much as possible. Most of the time, you can achieve the same effect by changing the enabled and input parameters in the initial request.

Trigger Options

OptionTypeDefaultDescription
paramsobject-Override URL path parameters
queryobject-Override query string parameters
bodyunknown-Override request body
forcebooleanfalseBypass cache and force fresh fetch

On-Demand Fetching

For cases where you don't want to fetch on mount (like download or print), use enabled: false and call trigger manually:

@Component({
  selector: "app-download-report",
  template: `
    <button (click)="download('monthly')" [disabled]="loading()">
      Download Monthly Report
    </button>
    <button (click)="download('yearly')" [disabled]="loading()">
      Download Yearly Report
    </button>
  `,
})
export class DownloadReportComponent {
  private report = injectRead(
    (api) => api("reports/:id").GET({ params: { id: "" } }), // placeholder
    { enabled: false }
  );

  loading = this.report.loading;

  async download(reportId: string) {
    const { data } = await this.report.trigger({ params: { id: reportId } });
    if (data) {
      // Process the downloaded report
      this.downloadFile(data.url, data.filename);
    }
  }

  private downloadFile(url: string, filename: string) {
    // Implementation
  }
}

On this page