Spoosh
Injects

Angular Injects

Angular inject functions for data fetching with full type safety

The @spoosh/angular package provides Angular inject functions for data fetching with full type safety using Signals.

Installation

npm install @spoosh/core @spoosh/angular

Setup

src/api/client.ts
import { Spoosh } from "@spoosh/core";
import { createAngularSpoosh } from "@spoosh/angular";
import { cachePlugin } from "@spoosh/plugin-cache";
import { invalidationPlugin } from "@spoosh/plugin-invalidation";
import { deduplicationPlugin } from "@spoosh/plugin-deduplication";

const client = new Spoosh<ApiSchema, Error>("/api").use([
  cachePlugin({ staleTime: 5000 }),
  invalidationPlugin({ defaultMode: "all" }),
  deduplicationPlugin({ read: "in-flight" }),
]);

export const { injectRead, injectWrite, injectInfiniteRead } =
  createAngularSpoosh(client);

Automatic Tag Generation

Spoosh automatically generates cache tags from API paths. These tags enable precise cache invalidation.

// api("users").GET()        → tags: ["users"]
// api("users/:id").GET({ params: { id: 1 } })     → tags: ["users", "users/1"]
// api("posts/:postId/comments").GET({ params: { postId: 5 } }) → tags: ["posts", "posts/5", "posts/5/comments"]

Tags are hierarchical. Invalidating "users" will refetch all queries under /users/*, while invalidating "users/1" only affects that specific user.

Custom Tags

The unified tags option supports modes and custom tags:

// Mode only - generates auto tags based on mode
users = injectRead((api) => api("users").GET(), {
  tags: "all", // Full hierarchy: ['users']
});

// Custom tags only - replaces auto-generated tags
users = injectRead((api) => api("users").GET(), {
  tags: ["custom-users", "dashboard"],
});

// Mode + custom tags - combines both
users = injectRead((api) => api("users").GET(), {
  tags: ["all", "dashboard-data"], // ['users', 'dashboard-data']
});

Cache Invalidation

The invalidationPlugin automatically refetches stale queries after mutations.

Auto Invalidation Modes

ModeDescription
"all"Invalidates all tags of the mutation path (default)
"self"Only invalidates the exact path
"none"No automatic invalidation
createPost = injectWrite((api) => api("posts").POST);

await createPost.trigger({
  body: { title: "New Post" },
  invalidate: "all",
});

Manual Invalidation

Target specific tags to invalidate:

createPost = injectWrite((api) => api("posts").POST);

await createPost.trigger({
  body: { title: "New Post" },
  invalidate: ["posts", "users/123/stats"],
});

You can also use mode + tags:

createPost = injectWrite((api) => api("posts").POST);

await createPost.trigger({
  body: { title: "New Post" },
  invalidate: ["posts", "dashboard"],
});

Request Deduplication

The deduplicationPlugin prevents duplicate network requests when the same query is called multiple times simultaneously.

Deduplication Modes

ModeDescription
"in-flight"Reuse pending request if same query is in progress (default for reads)
falseDisable deduplication
users = injectRead((api) => api("users").GET(), { dedupe: "in-flight" });

When multiple components call the same query simultaneously, only one network request is made:

@Component({
  selector: "app-header",
  template: `<div>{{ user.data()?.name }}</div>`,
})
export class HeaderComponent {
  user = injectRead((api) => api("me").GET());
}

@Component({
  selector: "app-sidebar",
  template: `<nav>{{ user.data()?.email }}</nav>`,
})
export class SidebarComponent {
  user = injectRead((api) => api("me").GET());
}

Available Injects

On this page