tanstack-query-craft
Version:
Type-safe module registry for TanStack Query with enhanced developer experience
386 lines (302 loc) β’ 9.11 kB
Markdown
# TanStack Query Craft
Type-safe module registration system for TanStack Query. Manage your queries and mutations centrally, enhance developer experience with full TypeScript support.
## Features
- β¨ **Full Type Safety**: Complete type support with TypeScript
- π― **Centralized Management**: Manage all your queries and mutations from a single place
- π **Easy Invalidation**: Perform query invalidation operations easily
- π¦ **Modular Structure**: Organize your projects in modules
- π **Enhanced DX**: Fast development with auto-completion and type inference
- π¨ **Flexible API**: Support for dynamic query keys and variables
## Installation
```bash
npm install tanstack-query-craft
```
## Basic Usage
### 1. Defining Queries and Mutations
```typescript
import {
defineQuery,
defineMutation,
defineModule,
} from "tanstack-query-craft";
// Define a query
const getUser = defineQuery<{ userId: string }, User>({
queryKey: (variables) => ["users", variables.userId],
queryFn: async (variables) => {
const response = await fetch(`/api/users/${variables.userId}`);
return response.json();
},
});
// Define a mutation
const createUser = defineMutation<{ name: string; email: string }, User>({
mutationKey: ["users", "create"],
mutationFn: async (variables) => {
const response = await fetch("/api/users", {
method: "POST",
body: JSON.stringify(variables),
});
return response.json();
},
});
// Create and register a module
export const userModule = defineModule("user", {
getUser,
createUser,
});
```
### 2. TypeScript Type Definitions
Define module registration types for full type support:
```typescript
// types/tanstack-query-craft.d.ts
import { userModule } from "../modules/user";
import { postModule } from "../modules/post";
declare module "tanstack-query-craft" {
interface TanstackQueryCraftRegistry {
user: typeof userModule;
post: typeof postModule;
}
}
```
### 3. Usage in React Components
```typescript
import { useQuery, useMutation } from "@tanstack/react-query";
import { getRegisteredQuery } from "tanstack-query-craft";
function UserProfile({ userId }: { userId: string }) {
// Using a query
const userQuery = useQuery(
getRegisteredQuery("user", "getUser", {
variables: { userId },
})
);
// Using a mutation
const createUserMutation = useMutation(
getRegisteredQuery("user", "createUser")
);
const handleCreateUser = () => {
createUserMutation.mutate({
name: "John Doe",
email: "john@example.com",
});
};
if (userQuery.isLoading) return <div>Loading...</div>;
if (userQuery.error) return <div>An error occurred!</div>;
return (
<div>
<h1>{userQuery.data.name}</h1>
<button onClick={handleCreateUser}>Create New User</button>
</div>
);
}
```
## Advanced Usage
### Query Invalidation
```typescript
import { useQueryClient } from "@tanstack/react-query";
import { invalidateQueries } from "tanstack-query-craft";
function MyComponent() {
const queryClient = useQueryClient();
const invalidate = invalidateQueries(queryClient);
const handleUpdate = async () => {
// Invalidate a single query
await invalidate.invalidate("user", "getUser", {
variables: { userId: "123" },
});
// Invalidate parent query (all user queries)
await invalidate.invalidate("user", "getUser", {
asParent: true,
});
// Invalidate by prefix
await invalidate.invalidateByPrefix(["users"]);
// Bulk invalidation
await invalidate.invalidateBulk([
{
moduleName: "user",
key: "getUser",
options: { variables: { userId: "1" } },
},
{
moduleName: "post",
key: "getPosts",
options: { variables: { page: 1 } },
},
]);
// Invalidate all queries
await invalidate.invalidateAll();
};
return <button onClick={handleUpdate}>Update</button>;
}
```
### Dynamic Query Keys
```typescript
const getPosts = defineQuery({
queryKey: (variables) => ["posts", variables.page, variables.limit],
queryFn: async (variables) => {
const response = await fetch(
`/api/posts?page=${variables.page}&limit=${variables.limit}`
);
return response.json();
},
});
```
### Static Query Keys
```typescript
const getAllUsers = defineQuery({
queryKey: ["users", "all"],
queryFn: async () => {
const response = await fetch("/api/users");
return response.json();
},
});
// Usage
const query = useQuery(getRegisteredQuery("user", "getAllUsers"));
```
### Getting Module Information
```typescript
import { getRegisteredModuleNames, getModuleKeys } from "tanstack-query-craft";
// Get all registered module names
const moduleNames = getRegisteredModuleNames();
console.log(moduleNames); // ['user', 'post', ...]
// Get all keys of a module
const userKeys = getModuleKeys("user");
console.log(userKeys); // { getUser: [Function], createUser: [Function], ... }
```
## Example Project Structure
```
src/
βββ modules/
β βββ user/
β β βββ queries.ts
β β βββ mutations.ts
β β βββ index.ts
β βββ post/
β β βββ queries.ts
β β βββ mutations.ts
β β βββ index.ts
β βββ index.ts
βββ types/
β βββ tanstack-query-craft.d.ts
βββ App.tsx
```
### modules/user/queries.ts
```typescript
import { defineQuery } from "tanstack-query-craft";
import { User } from "./types";
export const getUser = defineQuery<{ userId: string }, User>({
queryKey: (variables) => ["users", variables.userId],
queryFn: async (variables) => {
const response = await fetch(`/api/users/${variables.userId}`);
return response.json();
},
});
export const getUsers = defineQuery<{ page: number }, User[]>({
queryKey: (variables) => ["users", "list", variables.page],
queryFn: async (variables) => {
const response = await fetch(`/api/users?page=${variables.page}`);
return response.json();
},
});
```
### modules/user/mutations.ts
```typescript
import { defineMutation } from "tanstack-query-craft";
import { User } from "./types";
export const createUser = defineMutation<{ name: string; email: string }, User>(
{
mutationKey: ["users", "create"],
mutationFn: async (variables) => {
const response = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(variables),
});
return response.json();
},
}
);
export const updateUser = defineMutation<
{ userId: string; data: Partial<User> },
User
>({
mutationKey: (variables) => ["users", "update", variables.userId],
mutationFn: async (variables) => {
const response = await fetch(`/api/users/${variables.userId}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(variables.data),
});
return response.json();
},
});
```
### modules/user/index.ts
```typescript
import { defineModule } from "tanstack-query-craft";
import * as queries from "./queries";
import * as mutations from "./mutations";
export const userModule = defineModule("user", {
...queries,
...mutations,
});
```
## API Reference
### defineQuery
Used to define a query.
```typescript
defineQuery<TVariables, TData>(config: {
queryKey: QueryKey | ((variables: TVariables) => QueryKey);
queryFn: (variables: TVariables) => Promise<TData>;
})
```
### defineMutation
Used to define a mutation.
```typescript
defineMutation<TVariables, TData>(config: {
mutationKey: MutationKey | ((variables: TVariables) => MutationKey);
mutationFn: (variables: TVariables) => Promise<TData>;
})
```
### defineModule
Used to define and register a module.
```typescript
defineModule<TModuleName, TModule>(
name: TModuleName,
module: TModule
): TModule
```
### getRegisteredQuery
Used to retrieve a registered query or mutation.
```typescript
getRegisteredQuery<TModuleName, TKey, TOptions>(
moduleName: TModuleName,
key: TKey,
options?: TOptions
)
```
### invalidateQueries
Provides helper functions for query invalidation operations.
```typescript
invalidateQueries(queryClient: QueryClient): {
invalidate: (moduleName, key, options?) => Promise<void>;
invalidateByPrefix: (prefix: QueryKey) => Promise<void>;
invalidateAll: () => Promise<void>;
invalidateBulk: (queries: Array<...>) => Promise<void>;
queryClient: QueryClient;
}
```
### sanitizeKey
Cleans undefined, null, and empty string values from query keys.
```typescript
sanitizeKey(key: unknown[] | readonly unknown[]): readonly unknown[]
```
## Requirements
- TanStack Query (React Query) v5.0.0 or higher
- TypeScript 5.0 or higher
## License
MIT
## Contributing
We welcome your contributions! Feel free to submit pull requests.
## Author
[beratazgΓΌn](https://github.com/beratazgun)
## Links
- [GitHub Repository](https://github.com/beratazgun/tanstack-query-craft)
- [TanStack Query Documentation](https://tanstack.com/query/latest)