Spoosh
Plugins

Invalidation

Auto-invalidate queries after mutations

The invalidation plugin automatically refreshes related queries after mutations succeed. This keeps your UI in sync without manual refetching.

Installation

npm install @spoosh/plugin-invalidation

Usage

import { invalidationPlugin } from "@spoosh/plugin-invalidation";

const plugins = [
  cachePlugin({ staleTime: 5000 }),
  deduplicationPlugin(),
  invalidationPlugin(), // Auto-refresh queries after mutations
];

Default Configuration

// Default: invalidate all related tags (full hierarchy)
invalidationPlugin(); // same as { autoInvalidate: "all" }

// Only invalidate the exact endpoint by default
invalidationPlugin({ autoInvalidate: "self" });

// Disable auto-invalidation by default (manual only)
invalidationPlugin({ autoInvalidate: "none" });

How It Works

Tags are automatically generated from the API path hierarchy:

// Query tags are generated from the path:
useRead((api) => api.users.$get());
// → tags: ["users"]

useRead((api) => api.users[123].$get());
// → tags: ["users", "users/123"]

useRead((api) => api.users[123].posts.$get());
// → tags: ["users", "users/123", "users/123/posts"]

When a mutation succeeds, related queries are automatically invalidated:

const { trigger } = useWrite((api) => api.users[123].posts.$post);

await trigger({ body: { title: "New Post" } });
// ✓ Invalidates: "users", "users/123", "users/123/posts"
// All queries matching these tags will refetch automatically

Per-Request Override

Override the default invalidation behavior for specific mutations:

const { trigger } = useWrite((api) => api.posts.$post);

// Override to invalidate all related tags
await trigger({
  body: { title: "New Post" },
  autoInvalidate: "all",
});

// Override to only invalidate the exact endpoint
await trigger({
  body: { title: "New Post" },
  autoInvalidate: "self",
});

// Disable auto-invalidation and specify custom targets
await trigger({
  body: { title: "New Post" },
  autoInvalidate: "none",
  invalidate: (api) => [api.posts.$get, api.stats.$get, "dashboard-data"],
});

// Add specific tags (works alongside autoInvalidate)
await trigger({
  body: { title: "New Post" },
  invalidate: ["posts", "user-posts"],
});

Options

Plugin Config

OptionTypeDefaultDescription
autoInvalidate"all" | "self" | "none""all"Default auto-invalidation behavior

Per-Request Options

OptionTypeDescription
autoInvalidate"all" | "self" | "none"Override auto-invalidation behavior
invalidatestring[] | ((api) => [...])Specific tags or endpoints to invalidate

Auto-Invalidate Modes

ModeDescription
"all"Invalidate all tags from path hierarchy (default)
"self"Only invalidate the exact endpoint tag
"none"Disable auto-invalidation (manual only)

Example

// Path: users/123/posts
// Mode "all" invalidates: ["users", "users/123", "users/123/posts"]
// Mode "self" invalidates: ["users/123/posts"]
// Mode "none" invalidates: nothing (unless manually specified)

Combining with Deduplication

When invalidation triggers multiple queries to refetch, some may share the same endpoint. Use deduplicationPlugin to prevent duplicate network requests:

import { cachePlugin } from "@spoosh/plugin-cache";
import { deduplicationPlugin } from "@spoosh/plugin-deduplication";
import { invalidationPlugin } from "@spoosh/plugin-invalidation";

const plugins = [
  cachePlugin({ staleTime: 5000 }),
  deduplicationPlugin(), // Prevents duplicate requests during invalidation
  invalidationPlugin(),
];

On this page