@@ -837,12 +837,12 @@ import { useCreateCarMutation, useCarsQuery } from './generated/hooks';
837837
838838function CreateCarWithInvalidation() {
839839 const queryClient = useQueryClient ();
840-
840+
841841 const createCar = useCreateCarMutation ({
842842 onSuccess : () => {
843843 // Invalidate all car queries to refetch
844844 queryClient .invalidateQueries ({ queryKey: [' cars' ] });
845-
845+
846846 // Or invalidate specific queries
847847 queryClient .invalidateQueries ({ queryKey: [' cars' , { first: 10 }] });
848848 },
@@ -852,6 +852,151 @@ function CreateCarWithInvalidation() {
852852}
853853```
854854
855+ ### Centralized Query Keys
856+
857+ The codegen generates a centralized query key factory following the [ lukemorales query-key-factory] ( https://tanstack.com/query/docs/framework/react/community/lukemorales-query-key-factory ) pattern. This provides type-safe cache management with autocomplete support.
858+
859+ #### Generated Files
860+
861+ | File | Purpose |
862+ | ------| ---------|
863+ | ` query-keys.ts ` | Query key factories for all entities |
864+ | ` mutation-keys.ts ` | Mutation key factories for tracking in-flight mutations |
865+ | ` invalidation.ts ` | Type-safe cache invalidation helpers |
866+
867+ #### Using Query Keys
868+
869+ ``` tsx
870+ import { userKeys , invalidate } from ' ./generated/hooks' ;
871+ import { useQueryClient } from ' @tanstack/react-query' ;
872+
873+ // Query key structure
874+ userKeys .all // ['user']
875+ userKeys .lists () // ['user', 'list']
876+ userKeys .list ({ first: 10 }) // ['user', 'list', { first: 10 }]
877+ userKeys .details () // ['user', 'detail']
878+ userKeys .detail (' user-123' ) // ['user', 'detail', 'user-123']
879+
880+ // Granular cache invalidation
881+ const queryClient = useQueryClient ();
882+
883+ // Invalidate ALL user queries
884+ queryClient .invalidateQueries ({ queryKey: userKeys .all });
885+
886+ // Invalidate only list queries
887+ queryClient .invalidateQueries ({ queryKey: userKeys .lists () });
888+
889+ // Invalidate a specific user
890+ queryClient .invalidateQueries ({ queryKey: userKeys .detail (userId ) });
891+ ```
892+
893+ #### Invalidation Helpers
894+
895+ Type-safe invalidation utilities:
896+
897+ ``` tsx
898+ import { invalidate , remove } from ' ./generated/hooks' ;
899+
900+ // Invalidate queries (triggers refetch)
901+ invalidate .user .all (queryClient );
902+ invalidate .user .lists (queryClient );
903+ invalidate .user .detail (queryClient , userId );
904+
905+ // Remove from cache (for delete operations)
906+ remove .user (queryClient , userId );
907+ ```
908+
909+ #### Mutation Key Tracking
910+
911+ Track in-flight mutations with ` useIsMutating ` :
912+
913+ ``` tsx
914+ import { useIsMutating } from ' @tanstack/react-query' ;
915+ import { userMutationKeys } from ' ./generated/hooks' ;
916+
917+ function UserList() {
918+ // Check if any user mutations are in progress
919+ const isMutating = useIsMutating ({ mutationKey: userMutationKeys .all });
920+
921+ // Check if a specific user is being deleted
922+ const isDeleting = useIsMutating ({
923+ mutationKey: userMutationKeys .delete (userId )
924+ });
925+
926+ return (
927+ <div >
928+ { isMutating > 0 && <Spinner />}
929+ <button disabled = { isDeleting > 0 } >Delete</button >
930+ </div >
931+ );
932+ }
933+ ```
934+
935+ #### Optimistic Updates with Query Keys
936+
937+ ``` tsx
938+ import { useCreateUserMutation , userKeys } from ' ./generated/hooks' ;
939+
940+ const createUser = useCreateUserMutation ({
941+ onMutate : async (newUser ) => {
942+ // Cancel outgoing refetches
943+ await queryClient .cancelQueries ({ queryKey: userKeys .lists () });
944+
945+ // Snapshot previous value
946+ const previous = queryClient .getQueryData (userKeys .list ());
947+
948+ // Optimistically update cache
949+ queryClient .setQueryData (userKeys .list (), (old ) => ({
950+ ... old ,
951+ users: {
952+ ... old .users ,
953+ nodes: [... old .users .nodes , { id: ' temp' , ... newUser .input .user }]
954+ },
955+ }));
956+
957+ return { previous };
958+ },
959+ onError : (err , variables , context ) => {
960+ // Rollback on error
961+ queryClient .setQueryData (userKeys .list (), context .previous );
962+ },
963+ onSettled : () => {
964+ // Refetch after mutation
965+ queryClient .invalidateQueries ({ queryKey: userKeys .lists () });
966+ },
967+ });
968+ ```
969+
970+ #### Configuration
971+
972+ Query key generation is enabled by default. Configure in your config file:
973+
974+ ``` typescript
975+ // graphql-sdk.config.ts
976+ export default defineConfig ({
977+ endpoint: ' https://api.example.com/graphql' ,
978+
979+ queryKeys: {
980+ // Generate scope-aware keys (default: true)
981+ generateScopedKeys: true ,
982+
983+ // Generate mutation keys (default: true)
984+ generateMutationKeys: true ,
985+
986+ // Generate invalidation helpers (default: true)
987+ generateCascadeHelpers: true ,
988+
989+ // Define entity relationships for cascade invalidation
990+ relationships: {
991+ table: { parent: ' database' , foreignKey: ' databaseId' },
992+ field: { parent: ' table' , foreignKey: ' tableId' },
993+ },
994+ },
995+ });
996+ ```
997+
998+ For detailed documentation on query key factory design and implementation, see [ docs/QUERY-KEY-FACTORY.md] ( ./docs/QUERY-KEY-FACTORY.md ) .
999+
8551000### Prefetching
8561001
8571002``` tsx
0 commit comments