@sindika/query-toolkit
v0.1.0
Published
Tanstack Query Helper
Downloads
94
Readme
Nedo React Query Toolkit
A TypeScript-first TanStack Query wrapper designed specifically for Sindika applications. This library provides a standardized, type-safe way to handle API interactions with automatic CRUD operations, pagination, and state management.
Features
- 🔒 Type-Safe: Full TypeScript support with strict typing for resources and methods
- 🚀 CRUD Operations: Automatic Create, Read, Update, Delete operations
- 📄 Pagination: Built-in pagination with TanStack Table integration
- 🔄 Cache Management: Smart invalidation and cache management
- 🎯 Sindika Backend: Designed for Sindika's standardized response format
- 🪝 React Hooks: Easy-to-use React hooks for all operations
Installation
npm install @sindika/query-toolkitQuick Start
1. Create a Repository
Basic Repository (Full CRUD)
import type { RoleResponse } from "@/types/role/RoleResponse";
import type { RoleCreateRequest } from "@/types/role/RoleCreateRequest";
import type { RoleUpdateRequest } from "@/types/role/RoleUpdateRequest";
import type { RolePaginatedResponse } from "@/types/role/RolePaginatedResponse";
import { AxiosSingleton } from "@/api/axios/AxiosSingleton";
import {
type CrudTypes,
createRepository,
} from "@/lib/query-toolkit/createRepository";
type RoleRepository = CrudTypes<
RoleCreateRequest,
RoleUpdateRequest,
RoleResponse,
RolePaginatedResponse
>;
export const roleRepository = createRepository<RoleRepository>()({
collection: "role",
http: AxiosSingleton.getInstance(),
});Advanced Repository (Custom Methods)
import type { AxiosRequestConfig } from "axios";
import type BaseRequest from "@/types/common/BaseRequest";
import type BaseResponse from "@/types/common/BaseResponse";
import type { AuthTokenRequest } from "@/types/auth/AuthTokenRequest";
import type { AuthTokenResponse } from "@/types/auth/AuthTokenResponse";
import { AxiosSingleton } from "@/api/axios/AxiosSingleton";
import {
createRepository,
type RequestParam,
} from "@/lib/query-toolkit/createRepository";
export const authRepository = createRepository()({
only: [], // Disable default CRUD methods
collection: "auth",
http: AxiosSingleton.getInstance(),
extraMethods: ({ http, collection }) => ({
userInfo: (_: undefined, option?: AxiosRequestConfig) =>
http
.get<BaseResponse<AuthUserInfoResponse>>(`${collection}/userinfo`, {
signal: option?.signal,
})
.then((e) => e.data),
token: (
{ body: data }: RequestParam<BaseRequest<AuthTokenRequest>>,
option?: AxiosRequestConfig
) =>
http
.post<BaseResponse<AuthTokenResponse>>(`${collection}/token`, data, {
signal: option?.signal,
})
.then((e) => e.data),
}),
});2. Register Repositories
import { authRepository } from "./auth/authRepository";
import { roleRepository } from "./role/roleRepository";
import { userRepository } from "./user/userRepository";
export const repositories = {
auth: authRepository,
role: roleRepository,
user: userRepository,
} as const;
export type AppRepositories = typeof repositories;3. Create Query Toolkit Hooks
import { repositories } from "@/repository/repositories";
import { createUseOne } from "@/lib/query-toolkit/createUseOne";
import { createQueryOption } from "@/lib/query-toolkit/createQueryOption";
import { createInvalidation } from "@/lib/query-toolkit/createInvalidation";
import { createUsePaginationUrl } from "@/lib/query-toolkit/createUsePaginationUrl";
import { createUsePaginationInternal } from "@/lib/query-toolkit/createUsePaginationInternal";
import { createUseMutationCreateUpdate } from "@/lib/query-toolkit/createUseMutationCreateUpdateDelete";
export const usePaginationUrl = createUsePaginationUrl(repositories);
export const usePaginationInternal = createUsePaginationInternal(repositories);
export const useOne = createUseOne(repositories);
export const { useCreate, useDelete, useUpdate } =
createUseMutationCreateUpdate(repositories);
export const useInvalidation = createInvalidation(repositories);
export const appQueryOptions = createQueryOption(repositories);Usage Examples
Single Resource Queries
// Simple query without parameters
const { data: userInfoData } = useOne({
resource: "auth", // Type-safe resource selection
selectMethod: "userInfo",
});
// Query with parameters
const { data: single, isPending: isPendingSingle } = useOne({
resource: "profile",
selectMethod: "getSingle",
params: { pathParam: { id } },
});Pagination with URL State
// Pagination state stored in URL search params (using nuqs library)
const {
search,
refetch,
rowCount,
setSearch,
isFetching,
items: data,
tableState: { sorting, pagination },
tableCallbacks: { onSortingChange, onPaginationChange },
} = usePaginationUrl({
resource: "role",
selectMethod: "getListPagination",
getParams: (state) => ({ body: state }),
});Pagination with Internal State
// Pagination with internal useState
const {
search,
refetch,
rowCount,
setSearch,
isFetching,
items: data,
tableState: { sorting, pagination },
tableCallbacks: { onSortingChange, onPaginationChange },
} = usePaginationInternal({
resource: "profile",
selectMethod: "getListPagination",
getParams: (state) => ({ body: state }),
initialState: {
sorting: [
{
desc: false,
id: "name",
},
],
},
});Mutations (Create/Update/Delete)
const invalidation = useInvalidation();
const { mutate, isPending } = useCreate({
resource: "customEvent",
selectMethod: "create",
mutationOptions: {
onError: (error) => {
toast.error(error.message);
form.setServerError(error.error);
},
onSuccess: (data) => {
toast.success(data.message ?? "Custom event created successfully");
// Invalidate related queries
invalidation.invalidatePagination({
resource: "customEvent",
selectMethod: "getListPagination",
});
closeModal();
},
},
});
// Usage
const handleSubmit = (formData) => {
mutate({
params: { body: formData },
});
};Cache Invalidation
const invalidation = useInvalidation();
// Invalidate specific pagination
invalidation.invalidatePagination({
resource: "customEvent",
selectMethod: "getListPagination",
});
// Invalidate single resource
invalidation.invalidateOne({
resource: "user",
selectMethod: "getSingle",
params: { pathParam: { id: userId } },
});API Reference
Repository Types
CrudTypes<TCreate, TUpdate, TResponse, TPaginated>
TCreate: Type for create request payloadTUpdate: Type for update request payloadTResponse: Type for single resource responseTPaginated: Type for paginated list response
Repository Options
{
collection: string; // API collection name
http: AxiosInstance; // Axios instance
only?: CrudMethod[]; // Include only specific CRUD methods
except?: CrudMethod[]; // Exclude specific CRUD methods
extraMethods?: (config) => Methods; // Additional custom methods
}Hook Options
useOne Options
{
resource: keyof Repositories;
selectMethod: string;
params?: RequestParams;
queryOptions?: UseQueryOptions;
}usePagination Options
{
resource: keyof Repositories;
selectMethod: string;
getParams: (state) => RequestParams;
initialState?: TableState;
queryOptions?: UseQueryOptions;
}Mutation Options
{
resource: keyof Repositories;
selectMethod: string;
mutationOptions?: UseMutationOptions;
}TypeScript Support
This library provides full TypeScript support with:
- Resource Type Safety:
resourceparameter is strictly typed to repository keys - Method Type Safety:
selectMethodis typed based on available repository methods - Parameter Type Safety: Request parameters are typed based on method signatures
- Response Type Safety: All responses are properly typed based on repository definitions
Requirements
- TanStack Query v5+
- Axios
License
MIT
Contributing
This library is designed specifically for Sindika's backend architecture and response standards. If you need modifications for your use case, please fork the repository or create a custom wrapper.
