A framework-agnostic tool that automatically generates TypeScript types for your query keys by scanning your codebase for qk() calls.
- Framework Agnostic: Works with any JavaScript/TypeScript project
- Plugins: Vite and Webpack are currently supported
- CLI & Generic Plugin: CLI tool or a generic plugin for frameworks without a dedicated plugin
- Realtime updates: Automatic regeneration on file changes
npm install @frsty/typesafe-query-keysUse the CLI if there isn't a dedicated plugin for your framework. The CLI automatically picks config from your project root with names: queryKeys.config.{ts,js} or any rc style file with the name queryKeys
// queryKeys.config.ts
import { defineConfig } from "@frsty/typesafe-query-keys";
export default defineConfig({
include: ['src/**/*.queries.ts'],
exclude: ["**/temp", "**/.tanstack"],
functionNames: ['createQK', 'queryKey'],
ouputPath: ".generated/query-keys.d.ts",
verbose: true,
})# Generate types once
npx @frsty/typesafe-query-keys
# Watch for changes and regenerate automatically
npx @frsty/typesafe-query-keys --watch
# Or if you have it installed you can use
typesafe-query-keys
typesafe-query-keys --watch
# With config path
npx @frsty/typesafe-query-keys --config my-custom-config.config.ts"For Vite projects, you can use the the plugin.
// vite.config.ts
import { defineConfig } from "vite";
import typesafeQueryKeys from "@frsty/typesafe-query-keys/plugin/vite";
export default defineConfig({
plugins: [
typesafeQueryKeys({
include: ['src/**/*.queries.ts'],
exclude: ["**/temp", "**/.tanstack"],
functionNames: ['createQK', 'queryKey'],
ouputPath: ".generated/query-keys.d.ts",
verbose: true,
}),
],
});For projects that use webpack, you can use the the webpack plugin. e.g. Next.JS
// next.config.ts
import { NextConfig } from "next";
import typesafeQueryKeys from "@frsty/typesafe-query-keys/plugin/webpack";
export default {
webpack: (config) => {
config.plugins.push(
typesafeQueryKeys({
include: ['src/**/*.queries.ts'],
exclude: ["**/temp", "**/.tanstack"],
functionNames: ['createQK', 'queryKey'],
ouputPath: ".generated/query-keys.d.ts",
verbose: true,
}),
)
},
} satisfies NextConfigFor projects that don't use webpack or vite, you can use the generic plugin.
This is just an example for Next.JS using turbo but for other frameworks, make sure to call this plugin in the node environment, any file that is involved during the development process:
// next.config.ts
import { NextConfig } from "next";
import typesafeQueryKeys from "@frsty/typesafe-query-keys/plugin/generic";
typesafeQueryKeys({
include: ['src/**/*.queries.ts'],
exclude: ["**/temp", "**/.tanstack"],
functionNames: ['createQK', 'queryKey'],
ouputPath: ".generated/query-keys.d.ts",
verbose: true,
})
export default {
// Next config goes here
} satisfies NextConfig| Option | Type | Default | Description |
|---|---|---|---|
include |
string[] |
`[src/**/*.{ts,tsx,js,jsx}] | Glob patterns for files to scan |
outputPath |
string |
.generated/query-keys.gen.d.ts |
Output file for generated types |
exclude |
string[] |
["node_modules" "vite.config.*"] |
Additional ignore patterns |
functionNames |
string[] |
["qk"] |
Function names to extract query keys from (qk is always included) |
verbose |
boolean |
false |
Verbose mode for debugging |
The plugin:
- Scans your codebase for
qk()calls during build and development - Extracts query key patterns from the first argument of these calls
- Generates parent paths automatically (e.g.,
"users/$userId/posts"also registers"users"and"users/$userId") - Watches for file changes and regenerates types automatically
import { qk } from "@frsty/typesafe-query-keys";
import { useQuery } from "@tanstack/react-query"
// Define a query key pattern
const testQuery = useQuery({
// This automatically gets registered and generates types for parent paths as well: "users/$userId/posts", "users/$userId", "users"
queryKey: qk("users/$userId/posts", {
params: { userId: "123" }
}),
queryFn: async () => {
const response = await fetch(`https://api.example.com/users/${userId}/posts`);
return response.json();
}
})
// Use the registered pattern with `qk.use()` (with autocomplete)
const userPostsQK = qk.use("users/$userId/posts", {
params: { userId: "123" },
});
// Invalidate all user queries
queryClient.invalidateQueries({queryKey: qk.use("users")})- Check that your file matches the include patterns
- Verify the file is not being ignored
- Look for console logs from the plugin showing regeneration status
- Ensure the dev server is running.
- Ensure the generated types file is also included in your project's typescript config.
// tsconfig.json
{
"include": ["[PATH_TO_THE_GENERATED_TYPES_FILE]"]
}- Use more specific include patterns e.g. include only query files like "src/**/*.queries.ts"
- Add appropriate ignore patterns for large directories
- Consider excluding test files and build artifacts