{"_id":"@barehera/query-key-factory","_rev":"4-4baab281e2f43dfa2228e049eda967a7","name":"@barehera/query-key-factory","dist-tags":{"latest":"1.0.3"},"versions":{"1.0.0":{"name":"@barehera/query-key-factory","version":"1.0.0","keywords":["react-query","tanstack-query","query-keys","typescript","type-safe","cache","queryOptions"],"author":{"name":"barehera"},"license":"MIT","_id":"@barehera/query-key-factory@1.0.0","maintainers":[{"name":"barehera","email":"buyukavcilar.cagan@gmail.com"}],"homepage":"https://github.com/barehera/query-key-factory#readme","bugs":{"url":"https://github.com/barehera/query-key-factory/issues"},"dist":{"shasum":"c1b5961f260eb770139b6e307ce3b68b35d14f2e","tarball":"https://registry.npmjs.org/@barehera/query-key-factory/-/query-key-factory-1.0.0.tgz","fileCount":9,"integrity":"sha512-8E9NZvGyKTpM/V/Wc/t2LNeaYFPgld4aBuMPABzC9mmSo0YD0iYZXqzY+spELJIJ6vc8AGdPOy+VCbtSBs3V7g==","signatures":[{"sig":"MEQCIBy0GyJo2r4wcnwaCi1YGZEqhrBEfIVehFzJjJpIbD1SAiBK91MqX8OJlJwMF1hGyZddZv/Z5wpW06jUyiivVt713g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":35191},"main":"./dist/index.js","_from":"file:barehera-query-key-factory-1.0.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsup","test:watch":"vitest","type-check":"tsc --noEmit","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"barehera","email":"buyukavcilar.cagan@gmail.com"},"_resolved":"/private/var/folders/_j/8lh6jzvn7zbdqygksscn9h440000gn/T/d7e2021574932dd4ef7f4b7d7562c378/barehera-query-key-factory-1.0.0.tgz","_integrity":"sha512-8E9NZvGyKTpM/V/Wc/t2LNeaYFPgld4aBuMPABzC9mmSo0YD0iYZXqzY+spELJIJ6vc8AGdPOy+VCbtSBs3V7g==","repository":{"url":"git+https://github.com/barehera/query-key-factory.git","type":"git"},"_npmVersion":"11.5.1","description":"Type-safe query key factory for TanStack Query with queryOptions wrapper for complete type safety","directories":{},"_nodeVersion":"24.5.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.1","vitest":"^2.1.0","typescript":"^5.3.3","@types/node":"^20.10.0","@vitest/coverage-v8":"^2.1.0","@tanstack/react-query":"^5.59.0"},"peerDependencies":{"@tanstack/react-query":">=5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/query-key-factory_1.0.0_1760913298733_0.5117965555386346","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.0.1":{"name":"@barehera/query-key-factory","version":"1.0.1","keywords":["react-query","tanstack-query","query-keys","typescript","type-safe","cache","queryOptions"],"author":{"name":"barehera"},"license":"MIT","_id":"@barehera/query-key-factory@1.0.1","maintainers":[{"name":"barehera","email":"buyukavcilar.cagan@gmail.com"}],"homepage":"https://github.com/barehera/query-key-factory#readme","bugs":{"url":"https://github.com/barehera/query-key-factory/issues"},"dist":{"shasum":"516d6ad983d59af8bb06b199b66b166053c4ae5b","tarball":"https://registry.npmjs.org/@barehera/query-key-factory/-/query-key-factory-1.0.1.tgz","fileCount":9,"integrity":"sha512-b5kfjlSUUxDCOIMNgbNdm+6xZbEf/o2je8fAjrNG2yyXBqmLL0PDnqSieNjxbaNpkjROllVcNx8WP8eSYzADpQ==","signatures":[{"sig":"MEUCIQD7hnnxA8vOoZ5VQjarG5QqgA/mJeUmNVrGJjGu9FmP9gIgdHLu0Svuf6/VCkQ8QjBCuS6pEQ1aKdpgOUlgHjRQCb0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":33502},"main":"./dist/index.js","_from":"file:barehera-query-key-factory-1.0.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsup","test:watch":"vitest","type-check":"tsc --noEmit","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"barehera","email":"buyukavcilar.cagan@gmail.com"},"_resolved":"/private/var/folders/_j/8lh6jzvn7zbdqygksscn9h440000gn/T/4d2dbf71996a2d33ea15b292229164cc/barehera-query-key-factory-1.0.1.tgz","_integrity":"sha512-b5kfjlSUUxDCOIMNgbNdm+6xZbEf/o2je8fAjrNG2yyXBqmLL0PDnqSieNjxbaNpkjROllVcNx8WP8eSYzADpQ==","repository":{"url":"git+https://github.com/barehera/query-key-factory.git","type":"git"},"_npmVersion":"11.5.1","description":"Type-safe query key factory for TanStack Query with queryOptions wrapper for complete type safety","directories":{},"_nodeVersion":"24.5.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.1","vitest":"^2.1.0","typescript":"^5.3.3","@types/node":"^20.10.0","@vitest/coverage-v8":"^2.1.0","@tanstack/react-query":"^5.59.0"},"peerDependencies":{"@tanstack/react-query":">=5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/query-key-factory_1.0.1_1760913742206_0.23935401291689185","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.0.3":{"name":"@barehera/query-key-factory","version":"1.0.3","keywords":["react-query","tanstack-query","query-keys","typescript","type-safe","cache","queryOptions"],"author":{"name":"barehera"},"license":"MIT","_id":"@barehera/query-key-factory@1.0.3","maintainers":[{"name":"barehera","email":"buyukavcilar.cagan@gmail.com"}],"homepage":"https://github.com/barehera/query-key-factory#readme","bugs":{"url":"https://github.com/barehera/query-key-factory/issues"},"dist":{"shasum":"a3423a15ae677e99229703d4be37f2e223bc9ee4","tarball":"https://registry.npmjs.org/@barehera/query-key-factory/-/query-key-factory-1.0.3.tgz","fileCount":9,"integrity":"sha512-57K8RbUhb6GK0J4pc65igSvSVtYZD92rH+UViWfZHEhehpefnY/TUz0+8HrUBuFtw71V8jRmYxcyS915z3BGpA==","signatures":[{"sig":"MEUCIE6zMtBQRgnSldRAwuEVH3Tp2T47NYLbIENrSQgjEcDQAiEAjN4AAEgelcit+VkPc/Y0Gg61oqg0rVFfchxlmxkRF9I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@barehera%2fquery-key-factory@1.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":25532},"main":"./dist/index.js","_from":"file:barehera-query-key-factory-1.0.3.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsup","test:watch":"vitest","type-check":"tsc --noEmit","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"barehera","email":"buyukavcilar.cagan@gmail.com"},"_resolved":"/tmp/a0bfc589e050189b6d2699d1c3a0cbf5/barehera-query-key-factory-1.0.3.tgz","_integrity":"sha512-57K8RbUhb6GK0J4pc65igSvSVtYZD92rH+UViWfZHEhehpefnY/TUz0+8HrUBuFtw71V8jRmYxcyS915z3BGpA==","repository":{"url":"git+https://github.com/barehera/query-key-factory.git","type":"git"},"_npmVersion":"10.8.2","description":"Type-safe query key factory for TanStack Query with queryOptions wrapper for complete type safety","directories":{},"_nodeVersion":"20.19.5","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.1","vitest":"^2.1.0","typescript":"^5.3.3","@types/node":"^20.10.0","@vitest/coverage-v8":"^2.1.0","@tanstack/react-query":"^5.59.0"},"peerDependencies":{"@tanstack/react-query":">=5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/query-key-factory_1.0.3_1760987676804_0.843961945255602","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."}},"time":{"created":"2025-10-19T22:34:58.614Z","modified":"2026-01-31T22:52:53.113Z","1.0.0":"2025-10-19T22:34:58.907Z","1.0.1":"2025-10-19T22:42:22.414Z","1.0.3":"2025-10-20T19:14:37.018Z"},"bugs":{"url":"https://github.com/barehera/query-key-factory/issues"},"author":{"name":"barehera"},"license":"MIT","homepage":"https://github.com/barehera/query-key-factory#readme","keywords":["react-query","tanstack-query","query-keys","typescript","type-safe","cache","queryOptions"],"repository":{"url":"git+https://github.com/barehera/query-key-factory.git","type":"git"},"description":"Type-safe query key factory for TanStack Query with queryOptions wrapper for complete type safety","maintainers":[{"name":"barehera","email":"buyukavcilar.cagan@gmail.com"}],"readme":"<p align=\"center\">\n  <a href=\"https://github.com/barehera/query-key-factory\" target=\"\\_parent\"><img src=\"https://images.emojiterra.com/mozilla/512px/1f3ed.png\" alt=\"Factory emoji\" height=\"130\"></a>\n</p>\n\n<h1 align=\"center\">Query Key Factory</h1>\n\n<p align=\"center\">\n  <strong>Typesafe query key management for <a href=\"https://tanstack.com/query\" target=\"\\_parent\">@tanstack/query</a> with complete type safety across queryClient.</strong>\n</p>\n\n<p align=\"center\">\n  Built with <code>queryOptions</code> wrapper to ensure type-safe query key management<br/>throughout your entire application, from definitions to queryClient methods.\n</p>\n\n---\n\n> **Inspired by [@lukemorales/query-key-factory](https://github.com/lukemorales/query-key-factory)**  \n> This package builds upon the excellent ideas from Luke Morales' query-key-factory, with a key enhancement: wrapping all query configurations with `queryOptions` from `@tanstack/react-query` to provide complete type safety when using `queryClient` methods like `getQueryData`, `setQueryData`, and `invalidateQueries`.\n\n---\n\n## 🎯 Why This Package?\n\nThe key difference from other query key management solutions is the use of **`queryOptions`** wrapper, which ensures:\n\n- ✅ **Complete type safety** when using `queryClient.getQueryData(queryKey)` - TypeScript knows the exact return type\n- ✅ **Type-safe mutations** with `queryClient.setQueryData(queryKey, data)` - TypeScript validates the data structure\n- ✅ **Accurate invalidations** with `queryClient.invalidateQueries({ queryKey })` - no more runtime errors from mismatched keys\n- ✅ **Better developer experience** with autocomplete for query keys and data types throughout your app\n\n## 📦 Install\n\n```bash\nnpm install @barehera/query-key-factory\n```\n\n```bash\nyarn add @barehera/query-key-factory\n```\n\n```bash\npnpm add @barehera/query-key-factory\n```\n\n## ⚡ Quick Start\n\n### Declare your queries colocated by features\n\n```ts\n// queries/users.ts\nimport { createQueryKeys } from \"@barehera/query-key-factory\";\n\nexport const users = createQueryKeys(\"users\", {\n  all: {\n    queryKey: null,\n    queryFn: async () => api.getUsers(),\n  },\n  detail: (userId: string) => ({\n    queryKey: [userId],\n    queryFn: async () => api.getUser(userId),\n  }),\n});\n\n// queries/todos.ts\nexport const todos = createQueryKeys(\"todos\", {\n  detail: (todoId: string) => ({\n    queryKey: [todoId],\n    queryFn: async () => api.getTodo(todoId),\n  }),\n  list: (filters: TodoFilters) => ({\n    queryKey: [{ filters }],\n    queryFn: async () => api.getTodos(filters),\n    contextQueries: {\n      search: (query: string, limit = 15) => ({\n        queryKey: [query, limit],\n        queryFn: async () => api.searchTodos({ filters, query, limit }),\n      }),\n    },\n  }),\n});\n\n// queries/index.ts\nimport { mergeQueryKeys } from \"@barehera/query-key-factory\";\n\nexport const queries = mergeQueryKeys({ users, todos });\n```\n\n### Use throughout your codebase with complete type safety\n\n```ts\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { queries } from \"../queries\";\n\n// ✅ Simple queries\nexport function useUsers() {\n  return useQuery(queries.users.all);\n}\n\n// ✅ Dynamic queries\nexport function useUserDetail(id: string) {\n  return useQuery(queries.users.detail(id));\n}\n\n// ✅ Context queries for related data\nexport function useSearchTodos(filters: TodoFilters, query: string) {\n  return useQuery({\n    ...queries.todos.list(filters)._ctx.search(query),\n    enabled: Boolean(query),\n  });\n}\n\n// ✅ Type-safe mutations with queryClient\nexport function useUpdateTodo() {\n  const queryClient = useQueryClient();\n\n  return useMutation({\n    mutationFn: updateTodo,\n    onSuccess(newTodo) {\n      // ✅ TypeScript knows the exact type of data\n      queryClient.setQueryData(\n        queries.todos.detail(newTodo.id).queryKey,\n        newTodo\n      );\n\n      // ✅ Invalidate all todo list queries\n      queryClient.invalidateQueries({\n        queryKey: queries.todos.list._def,\n      });\n    },\n  });\n}\n```\n\n## 🔑 Complete Type Safety with queryClient\n\nThe key advantage of using `queryOptions` wrapper is **type safety when working with queryClient**:\n\n```ts\nconst queryClient = useQueryClient();\n\n// ❌ Without queryOptions wrapper (plain objects)\nconst data = queryClient.getQueryData([\"users\", \"detail\", userId]); \n// type: unknown - you have to manually cast\n\n// ✅ With queryOptions wrapper (this package)\nconst data = queryClient.getQueryData(queries.users.detail(userId).queryKey);\n// type: User - TypeScript infers the exact type from queryFn!\n\n// ✅ Type-safe setQueryData\nqueryClient.setQueryData(\n  queries.users.detail(userId).queryKey,\n  newUser // ✅ TypeScript validates this matches User type\n);\n\n// ✅ Type-safe invalidations with _def\nqueryClient.invalidateQueries({\n  queryKey: queries.users.detail._def, // ['users', 'detail']\n});\n```\n\n## 📝 Features\n\n### Standardized Query Keys\nAll keys follow @tanstack/query conventions with array format:\n\n```ts\nexport const todos = createQueryKeys(\"todos\", {\n  detail: (todoId: string) => ({\n    queryKey: [todoId],\n    queryFn: async () => api.getTodo(todoId),\n  }),\n  list: (filters: TodoFilters) => ({\n    queryKey: [{ filters }],\n    queryFn: async () => api.getTodos(filters),\n  }),\n});\n\n// Output:\n// {\n//   _def: ['todos'],\n//   detail: (todoId: string) => ({\n//     queryKey: ['todos', 'detail', todoId],\n//     queryFn: (ctx) => api.getTodo(todoId),\n//   }),\n//   list: (filters: TodoFilters) => ({\n//     queryKey: ['todos', 'list', { filters }],\n//     queryFn: (ctx) => api.getTodos(filters),\n//   }),\n// }\n```\n\n### Context Queries for Related Data\nDeclare queries that depend on a parent context:\n\n```ts\nexport const users = createQueryKeys(\"users\", {\n  detail: (userId: string) => ({\n    queryKey: [userId],\n    queryFn: async () => api.getUser(userId),\n    contextQueries: {\n      posts: {\n        queryKey: null,\n        queryFn: async () => api.getUserPosts(userId),\n      },\n      likes: (limit = 10) => ({\n        queryKey: [limit],\n        queryFn: async () => api.getUserLikes(userId, limit),\n      }),\n    },\n  }),\n});\n\n// Usage:\nfunction useUserPosts(userId: string) {\n  return useQuery(users.detail(userId)._ctx.posts);\n}\n\nfunction useUserLikes(userId: string, limit?: number) {\n  return useQuery(users.detail(userId)._ctx.likes(limit));\n}\n\n// Output:\n// users.detail('123')._ctx.posts.queryKey => ['users', 'detail', '123', 'posts']\n// users.detail('123')._ctx.likes(20).queryKey => ['users', 'detail', '123', 'likes', 20]\n```\n\n### Easy Invalidation with `_def`\nAccess query key scopes for invalidating multiple related queries:\n\n```ts\n// Invalidate all user detail queries\nqueryClient.invalidateQueries({\n  queryKey: queries.users.detail._def, // ['users', 'detail']\n});\n\n// Invalidate all todos\nqueryClient.invalidateQueries({\n  queryKey: queries.todos._def, // ['todos']\n});\n\n// Invalidate specific query\nqueryClient.invalidateQueries({\n  queryKey: queries.users.detail(\"123\").queryKey, // ['users', 'detail', '123']\n});\n```\n\n### Merge Query Keys from Multiple Features\n\n```ts\nimport { createQueryKeys, mergeQueryKeys } from \"@barehera/query-key-factory\";\n\n// Feature 1\nconst users = createQueryKeys(\"users\", {\n  all: { queryKey: null, queryFn: async () => api.getUsers() },\n});\n\n// Feature 2\nconst todos = createQueryKeys(\"todos\", {\n  all: { queryKey: null, queryFn: async () => api.getTodos() },\n});\n\n// Combine into single source of truth\nexport const queries = mergeQueryKeys({ users, todos });\n\n// Access:\nqueries.users.all.queryKey; // ['users', 'all']\nqueries.todos.all.queryKey; // ['todos', 'all']\n```\n\n## 🎓 Examples\n\n### Basic Query\n\n```ts\nconst users = createQueryKeys(\"users\", {\n  all: {\n    queryKey: null,\n    queryFn: async () => api.getUsers(),\n  },\n});\n\n// In component:\nfunction UserList() {\n  const { data } = useQuery(users.all);\n  // data is properly typed!\n}\n```\n\n### Dynamic Query with Parameters\n\n```ts\nconst todos = createQueryKeys(\"todos\", {\n  list: (filters: { status: string; priority: number }) => ({\n    queryKey: [{ filters }],\n    queryFn: async () => api.getTodos(filters),\n  }),\n});\n\n// In component:\nfunction TodoList() {\n  const filters = { status: \"active\", priority: 1 };\n  const { data } = useQuery(todos.list(filters));\n  // queryKey: ['todos', 'list', { filters: { status: 'active', priority: 1 } }]\n}\n```\n\n### Mutations with Cache Updates\n\n```ts\nfunction useCreateTodo() {\n  const queryClient = useQueryClient();\n\n  return useMutation({\n    mutationFn: api.createTodo,\n    onSuccess: (newTodo) => {\n      // ✅ Update specific todo in cache\n      queryClient.setQueryData(\n        queries.todos.detail(newTodo.id).queryKey,\n        newTodo\n      );\n\n      // ✅ Invalidate all list queries\n      queryClient.invalidateQueries({\n        queryKey: queries.todos.list._def,\n      });\n    },\n  });\n}\n```\n\n### Optimistic Updates\n\n```ts\nfunction useToggleTodo() {\n  const queryClient = useQueryClient();\n\n  return useMutation({\n    mutationFn: api.toggleTodo,\n    onMutate: async (todoId) => {\n      const queryKey = queries.todos.detail(todoId).queryKey;\n      \n      await queryClient.cancelQueries({ queryKey });\n      \n      // ✅ TypeScript knows the exact type\n      const previousTodo = queryClient.getQueryData(queryKey);\n      \n      // ✅ Optimistic update with type safety\n      queryClient.setQueryData(queryKey, {\n        ...previousTodo,\n        completed: !previousTodo?.completed,\n      });\n\n      return { previousTodo };\n    },\n    onError: (err, todoId, context) => {\n      // ✅ Rollback on error\n      queryClient.setQueryData(\n        queries.todos.detail(todoId).queryKey,\n        context?.previousTodo\n      );\n    },\n  });\n}\n```\n\n## 🆚 Comparison with Other Solutions\n\n### Without Query Key Factory\n```ts\n// ❌ Scattered key definitions\nconst userKeys = {\n  all: ['users'],\n  detail: (id: string) => ['users', id],\n};\n\n// ❌ No type safety\nconst data = queryClient.getQueryData(userKeys.detail(userId)); // type: unknown\n\n// ❌ Easy to make mistakes\nqueryClient.invalidateQueries({ queryKey: ['user', userId] }); // typo!\n```\n\n### With This Package\n```ts\n// ✅ Centralized definitions\nconst users = createQueryKeys('users', {\n  all: { queryKey: null, queryFn: async () => api.getUsers() },\n  detail: (userId: string) => ({\n    queryKey: [userId],\n    queryFn: async () => api.getUser(userId),\n  }),\n});\n\n// ✅ Complete type safety\nconst data = queryClient.getQueryData(users.detail(userId).queryKey); // type: User\n\n// ✅ Autocomplete prevents mistakes\nqueryClient.invalidateQueries({ queryKey: users.detail._def });\n```\n\n## 🙏 Credits\n\nThis package is inspired by and builds upon the excellent work of:\n- **[@lukemorales/query-key-factory](https://github.com/lukemorales/query-key-factory)** by [Luke Morales](https://github.com/lukemorales)\n\nThe core concept and API design are based on his original work. This package extends the idea by wrapping queries with `queryOptions` to provide complete type safety across queryClient operations.\n\n## 📄 License\n\nMIT License - Copyright (c) 2025 barehera\n\nSee [LICENSE](./LICENSE) for more information.\n\n---\n\n<p align=\"center\">\n  Made with ❤️ for the TanStack Query community\n</p>\n\n","readmeFilename":"README.md"}