Claude-skill-registry add-feature-hook
Creates TanStack Query hooks for API features with authentication. Use when connecting frontend to backend endpoints, creating data fetching hooks.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/add-feature-hook" ~/.claude/skills/majiayu000-claude-skill-registry-add-feature-hook && rm -rf "$T"
manifest:
skills/data/add-feature-hook/SKILL.mdsource content
Add Feature Hook
Creates TanStack Query hooks for API features with automatic authentication.
Prerequisites
Generate latest API types (backend must be running):
cd front && pnpm run generate:api
3-Layer API Pattern
NEVER call API directly in components!
Feature Hooks (use-items.ts) ↓ uses Base Auth Hooks (use-api.ts: useAuthenticatedQuery/Mutation) ↓ uses Generated SDK (lib/api/*.gen.ts from OpenAPI)
Workflow
1. Create Hook File
// lib/hooks/use-my-feature.ts import { useSuspenseQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useAuthenticatedQuery, useAuthenticatedMutation } from './use-api'; import { getMyFeatures, createMyFeature, updateMyFeature, deleteMyFeature, } from '@/lib/api/sdk.gen'; import type { CreateMyFeatureRequest, MyFeatureResponse } from '@/lib/api/types.gen'; // Query key factory - prevents cache bugs export const myFeatureKeys = { all: ['my-feature'] as const, list: (userId?: string) => [...myFeatureKeys.all, 'list', userId] as const, detail: (id: string) => [...myFeatureKeys.all, 'detail', id] as const, };
2. Add Query Hook
// Regular query (for optional data) export function useMyFeatures(userId?: string) { return useAuthenticatedQuery({ queryKey: myFeatureKeys.list(userId), queryFn: async (token) => { const response = await getMyFeatures({ headers: { Authorization: `Bearer ${token}` }, }); return response.data ?? []; }, enabled: !!userId, }); } // Suspense query (for required data - use in content components) export function useMyFeaturesSuspense(userId: string) { return useAuthenticatedQuery({ queryKey: myFeatureKeys.list(userId), queryFn: async (token) => { const response = await getMyFeatures({ headers: { Authorization: `Bearer ${token}` }, }); return response.data ?? []; }, suspense: true, // Enables Suspense mode }); }
3. Add Mutation Hooks
export function useCreateMyFeature() { const queryClient = useQueryClient(); return useAuthenticatedMutation({ mutationFn: async (data: CreateMyFeatureRequest, token) => { const response = await createMyFeature({ headers: { Authorization: `Bearer ${token}` }, body: data, }); return response.data; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: myFeatureKeys.all }); }, showSuccessToast: 'Created successfully!', showErrorToast: true, // Default error handling }); } export function useDeleteMyFeature() { const queryClient = useQueryClient(); return useAuthenticatedMutation({ mutationFn: async (id: string, token) => { await deleteMyFeature({ headers: { Authorization: `Bearer ${token}` }, path: { id }, }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: myFeatureKeys.all }); }, showSuccessToast: 'Deleted successfully!', }); }
Query Key Factory
Always use query key factories to prevent cache bugs:
export const myFeatureKeys = { all: ['my-feature'] as const, list: (userId?: string) => [...myFeatureKeys.all, 'list', userId] as const, detail: (id: string) => [...myFeatureKeys.all, 'detail', id] as const, filtered: (filters: Filters) => [...myFeatureKeys.all, 'filtered', filters] as const, };
Usage in Components
// Content component (with Suspense) function MyFeatureContent({ userId }: { userId: string }) { const { data } = useMyFeaturesSuspense(userId); const createMutation = useCreateMyFeature(); const handleCreate = async (data: CreateMyFeatureRequest) => { await createMutation.mutateAsync(data); }; return ( <div> {data.map((item) => <div key={item.id}>{item.name}</div>)} <button onClick={() => handleCreate({ name: 'New' })}> Create </button> </div> ); }
Toast Notifications
Automatic via
useAuthenticatedMutation:
- Success: Green toast with custom message
- Error: Automatic error handling with red toast
- 401: Auto-redirect to login
DO NOT add manual error handling in components.
Generated API Types
Types are auto-generated from backend OpenAPI:
// Import from generated types import type { CreateMyFeatureRequest, MyFeatureResponse } from '@/lib/api/types.gen'; // Import API functions import { getMyFeatures, createMyFeature } from '@/lib/api/sdk.gen';
DO NOT edit files in
lib/api/ - regenerate instead.