@kellanjs/keycraft
v0.1.0
Published
Type-safe query key manager for TanStack Query and other state management libraries.
Maintainers
Readme
keycraft
Type-safe query key builder for TanStack Query and other cache/state management libraries.
keycraft helps you define query keys as a structured tree instead of hand-writing string arrays everywhere. It gives you:
- readable query key definitions
- type-safe nested access
- parameterized keys with
$scope - reusable segments with
segment()
Install
npm install @kellanjs/keycraftQuick start
import { keycraft } from "@kellanjs/keycraft";
const k = keycraft({
users: {
all: null,
userId: {
$scope: (id: string) => id,
posts: { all: null },
},
},
});
k.users.all.$key;
// ["users", "all"]
k.users.userId("123").$key;
// ["users", "userId", "123"]
k.users.userId("123").posts.all.$key;
// ["users", "userId", "123", "posts", "all"]Core concepts
1. Build a key tree
Each top-level property becomes a root key segment.
const k = keycraft({
posts: { recent: null },
});
k.posts.recent.$key;
// ["posts", "recent"]2. Use null or {} for leaf nodes
Both represent a key with no children.
const k = keycraft({
users: {
all: null,
recent: {},
},
});3. Use $scope for parameterized segments
$scope makes a node callable.
const k = keycraft({
users: {
userId: {
$scope: (id: string) => id,
},
},
});
k.users.userId("abc").$key;
// ["users", "userId", "abc"]You can use any value that makes sense for your query key, including numbers or objects.
const k = keycraft({
search: {
filter: {
$scope: (params: { status: string; role: string }) =>
`${params.status}-${params.role}`,
results: null,
},
},
});
k.search.filter({ status: "active", role: "admin" }).results.$key;
// ["search", "filter", "active-admin", "results"]4. Reuse parts of the tree with segment()
If you want to define a segment once and reuse it in multiple places, use segment().
import { keycraft, segment } from "@kellanjs/keycraft";
const pagination = segment({
page: {
$scope: (page: number) => page,
},
});
const k = keycraft({
users: {
all: pagination,
},
posts: {
all: pagination,
},
});
k.users.all.page(1).$key;
// ["users", "all", "page", 1]
k.posts.all.page(2).$key;
// ["posts", "all", "page", 2]API
keycraft(definition)
Creates a typed key tree from a definition object.
import { keycraft } from "@kellanjs/keycraft";
const k = keycraft({
todos: { all: null },
});segment(definition)
Identity helper for defining reusable segments with preserved type inference.
import { segment } from "@kellanjs/keycraft";
const userSegment = segment({
$scope: (id: string) => id,
posts: { all: null },
});Type exports
The package also exports useful TypeScript types:
QueryKeyKeycraftKeysInferNodeSegmentDefinition
Notes
$keyis readonly and non-enumerable.- Child nodes still work on scoped nodes.
- Properties starting with
$are reserved for internal metadata and are ignored bykeycraft().
Example: TanStack Query
import { useQuery } from "@tanstack/react-query";
import { keycraft } from "@kellanjs/keycraft";
const k = keycraft({
users: {
all: null,
userId: {
$scope: (id: string) => id,
profile: null,
},
},
});
function UserProfile({ userId }: { userId: string }) {
const query = useQuery({
queryKey: k.users.userId(userId).profile.$key,
queryFn: async () => {
// fetch user profile
},
});
return null;
}Development
npm run ciThis runs linting, type-checking, build, formatting checks, export validation, and tests.
Thanks
If you made it this far, thanks for checking out the library, and I hope you find it useful in your projects!
License
Keycraft is open source under the terms of the MIT license.
