npm package: @sindika/query-toolkit
Description: Tanstack Query Helper
Author: Willy Achmat Fauzi
Latest version: 0.1.0

# 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

```bash
npm install @sindika/query-toolkit
```

## Quick Start

### 1. Create a Repository

#### Basic Repository (Full CRUD)

```typescript
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)

```typescript
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

```typescript
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

```typescript
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

```typescript
// 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

```typescript
// 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

```typescript
// 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)

```typescript
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

```typescript
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 payload
- `TUpdate`: Type for update request payload
- `TResponse`: Type for single resource response
- `TPaginated`: Type for paginated list response

#### Repository Options

```typescript
{
  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

```typescript
{
  resource: keyof Repositories;
  selectMethod: string;
  params?: RequestParams;
  queryOptions?: UseQueryOptions;
}
```

#### usePagination Options

```typescript
{
  resource: keyof Repositories;
  selectMethod: string;
  getParams: (state) => RequestParams;
  initialState?: TableState;
  queryOptions?: UseQueryOptions;
}
```

#### Mutation Options

```typescript
{
  resource: keyof Repositories;
  selectMethod: string;
  mutationOptions?: UseMutationOptions;
}
```

## TypeScript Support

This library provides full TypeScript support with:

- **Resource Type Safety**: `resource` parameter is strictly typed to repository keys
- **Method Type Safety**: `selectMethod` is 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.