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/angularSetup
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
| Mode | Description |
|---|---|
"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
| Mode | Description |
|---|---|
"in-flight" | Reuse pending request if same query is in progress (default for reads) |
false | Disable 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
- injectRead - Fetch data with automatic caching
- injectWrite - Trigger mutations with loading states
- injectInfiniteRead - Paginated data fetching