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 { create } from "@spoosh/angular";
import { cachePlugin } from "@spoosh/plugin-cache";
import { invalidationPlugin } from "@spoosh/plugin-invalidation";
import { deduplicationPlugin } from "@spoosh/plugin-deduplication";

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

export const { injectRead, injectWrite, injectPages, injectQueue } =
  create(spoosh);

Automatic Tag Generation

Spoosh automatically generates a cache tag from the resolved API path.

// api("users").GET()                              → tag: "users"
// api("users/:id").GET({ params: { id: 1 } })     → tag: "users/1"
// api("posts/:postId/comments").GET({ params: { postId: 5 } }) → tag: "posts/5/comments"

Custom Tags

Override the auto-generated tag with custom tags:

// Single custom tag
users = injectRead((api) => api("users").GET(), {
  tags: "my-users",
});

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

Cache Invalidation

The invalidationPlugin automatically refetches queries after mutations using wildcard pattern matching.

Pattern Matching

PatternMatches
"posts"Exact match only
"posts/*"All children (posts/1, posts/1/comments)
["posts", "posts/*"]posts AND all children (default)
createPost = injectWrite((api) => api("posts").POST());

// Default behavior — invalidates ["posts", "posts/*"]
await createPost.trigger({ body: { title: "New Post" } });

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

// Disable invalidation
await createPost.trigger({
  body: { title: "New Post" },
  invalidate: false,
});

Manual Invalidation

Target specific patterns to invalidate:

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

await createPost.trigger({
  body: { title: "New Post" },
  invalidate: ["posts", "users/*", "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