Error in user YAML: (<unknown>): did not find expected alphabetic or numeric character while scanning an alias at line 2 column 8
---
description: Guidelines for Mutation Hooks
globs: **/hooks/query/**/*.ts, **/_hooks/query/**/*.ts
alwaysApply: false
---Mutation hooks abstract data modification operations (create, update, delete) using TanStack Query's useMutation hook. They provide loading states, error handling, and optimistic updates. These hooks connect UI components to the data access layer for write operations, keeping components focused on presentation rather than data manipulation logic.
src/
├── hooks/ # Shared hooks module
│ └── query/ # Shared query hooks (includes mutations)
│ └── [entity]/
│ ├── use-create-[entity]-mutation.ts # Create operation
│ ├── use-update-[entity]-mutation.ts # Update operation
│ ├── use-delete-[entity]-mutation.ts # Delete operation
│ └── use-[specific-action]-[entity]-mutation.ts # Other specific mutations
└── features/
└── [feature-name]/
└── _hooks/ # Feature-specific hooks
└── query/ # Feature-specific query hooks (includes mutations)
└── [entity]/
├── use-create-[entity]-mutation.ts
├── use-update-[entity]-mutation.ts
└── use-delete-[entity]-mutation.ts
use-create-[entity]-mutation.ts: For create operationsuse-update-[entity]-mutation.ts: For update operationsuse-delete-[entity]-mutation.ts: For delete operationsuse-[specific-action]-[entity]-mutation.ts: For other specific actions (e.g.,use-set-active-[entity]-mutation.ts)
useCreate[Entity]Mutation: For create operationsuseUpdate[Entity]Mutation: For update operationsuseDelete[Entity]Mutation: For delete operationsuse[SpecificAction][Entity]Mutation: For other specific actions
UseCreate[Entity]MutationArgs: Arguments for create mutation hookUseUpdate[Entity]MutationArgs: Arguments for update mutation hookUseDelete[Entity]MutationArgs: Arguments for delete mutation hookUse[SpecificAction][Entity]MutationArgs: Arguments for specific action mutation hook
- Use TanStack Query's
useMutationhook - Import mutation functions directly from
@/data - Use TypeScript's
MutationOptionstype for args - Spread args at the beginning to allow overriding any option
- Always get
queryClientusinguseQueryClient() - Handle cache invalidation in
onSuccess - Handle errors in
onErrorwith optional error alerting utility - Call the provided callback after internal logic
// use-create-[entity]-mutation.ts
import { type MutationOptions, useMutation, useQueryClient } from '@tanstack/react-query';
import { post[Entity]s, type Post[Entity]sData, type Post[Entity]sResponse } from '@/data';
import { alertApiError } from '@/utils/api-errors';
export type UseCreate[Entity]MutationArgs = MutationOptions<
Post[Entity]sResponse,
Error,
Post[Entity]sData
>;
export function useCreate[Entity]Mutation(args: UseCreate[Entity]MutationArgs = {}) {
const queryClient = useQueryClient();
return useMutation({
...args,
mutationFn: post[Entity]s,
onSuccess: async (data, variables, context) => {
await queryClient.invalidateQueries({ queryKey: ['/[entity]s'] });
args.onSuccess?.(data, variables, context);
},
onError: (error, variables, context) => {
if (args?.onError) return args.onError(error, variables, context);
alertApiError(error);
},
});
}// use-update-[entity]-mutation.ts
import { type MutationOptions, useMutation, useQueryClient } from '@tanstack/react-query';
import {
put[Entity]sBy[Entity]Id,
type Put[Entity]sBy[Entity]IdData,
type Put[Entity]sBy[Entity]IdResponse,
} from '@/data';
import { alertApiError } from '@/utils/api-errors';
export type UseUpdate[Entity]MutationArgs = MutationOptions<
Put[Entity]sBy[Entity]IdResponse,
Error,
Put[Entity]sBy[Entity]IdData
>;
export function useUpdate[Entity]Mutation(args: UseUpdate[Entity]MutationArgs = {}) {
const queryClient = useQueryClient();
return useMutation({
...args,
mutationFn: put[Entity]sBy[Entity]Id,
onSuccess: async (data, variables, context) => {
await queryClient.invalidateQueries({ queryKey: ['/[entity]s'] });
args.onSuccess?.(data, variables, context);
},
onError: (error, variables, context) => {
if (args?.onError) return args.onError(error, variables, context);
alertApiError(error);
},
});
}// use-delete-[entity]-mutation.ts
import { type MutationOptions, useMutation, useQueryClient } from '@tanstack/react-query';
import {
delete[Entity]sBy[Entity]Id,
type Delete[Entity]sBy[Entity]IdData,
type Delete[Entity]sBy[Entity]IdResponse,
} from '@/data';
import { alertApiError } from '@/utils/api-errors';
export type UseDelete[Entity]MutationArgs = MutationOptions<
Delete[Entity]sBy[Entity]IdResponse,
Error,
Delete[Entity]sBy[Entity]IdData
>;
export function useDelete[Entity]Mutation(args: UseDelete[Entity]MutationArgs = {}) {
const queryClient = useQueryClient();
return useMutation({
...args,
mutationFn: delete[Entity]sBy[Entity]Id,
onSuccess: async (data, variables, context) => {
await queryClient.invalidateQueries({ queryKey: ['/[entity]s'] });
args.onSuccess?.(data, variables, context);
},
onError: (error, variables, context) => {
if (args?.onError) return args.onError(error, variables, context);
alertApiError(error);
},
});
}src/hooks/query/[entity]/
├── use-create-[entity]-mutation.ts # Create
├── use-update-[entity]-mutation.ts # Update
└── use-delete-[entity]-mutation.ts # Delete
- Implement appropriate cache invalidation in onSuccess
- Consider which queries need to be invalidated after a mutation
- Use queryClient.invalidateQueries for cache invalidation
- Implement appropriate error handling strategies
- Forward errors to the consuming components
- Consider global error handling for common error scenarios
- Keep side effects (like showing toast notifications) in the component using the mutation
- Use the onSuccess and onError callbacks for side effects
- Expose and use mutation states (isLoading, isError, isSuccess) in UI components
- Handle different states appropriately in the UI