### Install Dependencies Source: https://github.com/ronasit/rtkq-entity-api/blob/main/CONTRIBUTING.md Run this command in the project root to install all necessary development dependencies. ```bash npm install ``` -------------------------------- ### Install @ronas-it/rtkq-entity-api Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Install the library using npm. For React Native, also install the optional peer dependency. ```bash npm i @ronas-it/rtkq-entity-api # React Native only (optional peer dependency): npm i @react-native-community/netinfo ``` -------------------------------- ### Install RTK Query Entity API Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Install the package using npm. ```sh npm i @ronas-it/rtkq-entity-api ``` -------------------------------- ### Install netinfo for React Native Refetch Listeners Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Before using `setupRefetchListeners`, install the `@react-native-community/netinfo` package for network status detection in React Native applications. ```bash npm i @react-native-community/netinfo ``` -------------------------------- ### Setup Refetch Listeners for React Native Apps Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Wraps RTK Query's setupListeners with AppState and NetInfo events for automatic query refetching on app focus or network reconnection. Throws if called on web; use RTK Query's setupListeners for web. ```typescript import { setupRefetchListeners } from '@ronas-it/rtkq-entity-api'; import { addEventListener, fetch } from '@react-native-community/netinfo'; import ReactNative, { AppState, Platform } from 'react-native'; import { useDispatch } from 'react-redux'; import { useEffect } from 'react'; function App() { const dispatch = useDispatch(); useEffect(() => { const unsubscribe = setupRefetchListeners( dispatch, { refetchOnFocus: true, refetchOnReconnect: true }, { addEventListener, fetch }, // NetInfo API { AppState, Platform }, // React Native refs ); return unsubscribe; // cleans up all listeners on unmount }, []); return ; } // Throws if called on web — use RTK Query's own setupListeners() for web apps. ``` -------------------------------- ### Update User with Pessimistic Cache Patching Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Updates a user's information using `useUpdateMutation`. This example demonstrates automatic, pessimistic cache patching where the server response is merged into the cache after a successful update. Ensure `usersApi` is imported. ```typescript import { usersApi } from './users-api'; function EditUserForm({ user }: { user: User }) { const [updateUser, { isLoading }] = usersApi.useUpdateMutation(); const save = async (name: string) => { try { const updated = await updateUser({ id: user.id, name }).unwrap(); // All search/get queries that hold this user are automatically patched — no refetch needed. console.log('Saved:', updated.name); } catch (err) { console.error('Update failed:', err.message); } }; } ``` -------------------------------- ### Setup Automatic Refetching in React Native Apps Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Use `setupRefetchListeners` in a root component to automatically refetch data when the app regains focus or reconnects to the internet. This utility requires `@react-native-community/netinfo` and should only be used in React Native environments. ```tsx import { addEventListener, fetch } from '@react-native-community/netinfo'; import { setupRefetchListeners } from '@ronas-it/rtkq-entity-api'; import ReactNative from 'react-native'; import { useDispatch } from 'react-redux'; function App(): ReactElement { const dispatch = useDispatch(); useEffect(() => { const unsubscribeRefetchListeners = setupRefetchListeners( dispatch, { refetchOnFocus: true, refetchOnReconnect: true }, { addEventListener, fetch }, ReactNative, ); return unsubscribeRefetchListeners; }, []); ... } ``` -------------------------------- ### fetchEntity Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Utility to fetch single entity data using GET /{baseEndpoint}/{id} with optional parameters. Useful for customizing `onQueryStarted` behavior. ```APIDOC ## fetchEntity ### Description Fetches single entity data using `GET /{baseEndpoint}/{id}` with optional parameters. ### Method GET ### Endpoint `/{baseEndpoint}/{id}` ### Parameters #### Path Parameters - **id** (string | number) - Required - The ID of the entity to fetch. #### Query Parameters - **params** (object) - Optional - Additional parameters for the fetch request (e.g., `relations`). ### Request Example ```ts const someExtendedRequest = { id: createdEntity.id, relations: ['photos'] }; const fullEntity = await someItemApi.util.fetchEntity(createdEntity.id, someExtendedRequest, { dispatch }); ``` ### Response #### Success Response (200) - **entity** (object) - The fetched entity data. ``` -------------------------------- ### Search Endpoint Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Performs a GET request to search entities with serialised query parameters from a PaginationRequest instance. Returns a PaginationResponse with a typed data array and pagination metadata. ```APIDOC ## GET /baseEndpoint ### Description Performs a GET request to search entities with serialised query parameters from a PaginationRequest instance. Returns a PaginationResponse with a typed data array and pagination metadata. ### Method GET ### Endpoint /baseEndpoint ### Query Parameters - **page** (number) - Required - The page number for pagination. - **perPage** (number) - Required - The number of items per page. - **orderBy** (string) - Required - The field to order the results by. - **query** (string) - Optional - The search query string. ### Response #### Success Response (200) - **data** (array) - An array of entity objects. - **pagination** (object) - Pagination metadata including currentPage and lastPage. ``` -------------------------------- ### Endpoint: `get` Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Performs a `GET` request to retrieve a specific entity by its ID. It supports optional query parameters for loading relations and deserializes the response into a typed entity instance. ```APIDOC ## Endpoint: `get` Performs `GET /baseEndpoint/:id`. Accepts `{ id, params? }` where `params` is an optional `EntityRequest` instance for relation loading. Response is deserialised to a typed entity. ### Usage ```ts import { usersApi } from './users-api'; import { UserEntityRequest } from './models/requests'; function UserProfile({ id }: { id: number }) { const { data: user, isLoading, error } = usersApi.useGetQuery({ id, params: new UserEntityRequest({ relations: ['posts', 'profile'] }), // → GET /users/42?with[]=posts&with[]=profile }); if (isLoading) return ; if (error) return ; return
{user.name} — joined {user.createdAt.toLocaleString()}
; } ``` ``` -------------------------------- ### Get Endpoint Query Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Performs a `GET` request to `baseEndpoint/:id` to retrieve a single entity. Accepts an object with `id` and optional `params` for query parameters. The response is deserialized into a typed entity. ```typescript import { usersApi } from './users-api'; import { UserEntityRequest } from './models/requests'; function UserProfile({ id }: { id: number }) { const { data: user, isLoading, error } = usersApi.useGetQuery({ id, params: new UserEntityRequest({ relations: ['posts', 'profile'] }), // → GET /users/42?with[]=posts&with[]=profile }); if (isLoading) return ; if (error) return ; return
{user.name} — joined {user.createdAt.toLocaleString()}
; ``` -------------------------------- ### Fetch Single Entity Data with RTKQ Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Use `fetchEntity` to retrieve single entity data via GET requests. It's useful for customizing `onQueryStarted` behavior by fetching extended entity data. ```typescript async onQueryStarted(_, { queryFulfilled, dispatch }) { // Wait for mutation to success: const { data: createdEntity } = await queryFulfilled; // Fetch extended entity data: const someExtendedRequest = { id: createdEntity.id, relations: ['photos'] }; const fullEntity = await someItemApi.util.fetchEntity( createdEntity.id, someExtendedRequest, { dispatch } ); // Prefill 'get' query for certain params: someItemApi.util.upsertQueryData('get', someExtendedRequest, fullEntity); } ``` -------------------------------- ### Get Running Queries for APIs in SSR Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Resolves all in-flight RTK Query requests for specified APIs. Essential for Next.js SSR to ensure the Redux store is hydrated before serializing server-side props. ```typescript import { getRunningQueriesForAPIs } from '@ronas-it/rtkq-entity-api'; import { store } from './store'; import { usersApi } from './users-api'; import { postsApi } from './posts-api'; // In a Next.js getServerSideProps: export async function getServerSideProps(context) { store.dispatch(usersApi.endpoints.search.initiate(new UserSearchRequest({ page: 1 }))); store.dispatch(postsApi.endpoints.search.initiate(new PostSearchRequest({}))); // Wait for ALL running queries across both APIs to complete: await getRunningQueriesForAPIs(store, [usersApi, postsApi]); return { props: { initialReduxState: store.getState() } }; } ``` -------------------------------- ### api.util.clearEntityQueries Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Removes an entity from every cached list/infinite-list query containing it and decrements `pagination.total`. Does nothing to single-entity `get` cache entries. ```APIDOC ## `api.util.clearEntityQueries` Removes an entity from every cached list/infinite-list query containing it and decrements `pagination.total`. Does nothing to single-entity `get` cache entries. ```ts const removeApi = usersApi.injectEndpoints({ endpoints: (builder) => ({ removeFromGroup: builder.mutation({ query: ({ userId, groupId }) => ({ method: 'delete', url: `/groups/${groupId}/users/${userId}` }), async onQueryStarted({ userId }, apiLifecycle) { await apiLifecycle.queryFulfilled; await usersApi.util.clearEntityQueries( userId, apiLifecycle, { tags: [{ type: 'user', id: 'group-members' }] }, ); }, }), }), }); ``` ``` -------------------------------- ### Delete Endpoint Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Performs a DELETE request to remove an entity by its ID. On success, removes the entity from all cached lists and get queries without triggering refetches. ```APIDOC ## DELETE /baseEndpoint/:id ### Description Performs a DELETE request to remove an entity by its ID. On success, removes the entity from all cached lists and get queries without triggering refetches. ### Method DELETE ### Endpoint /baseEndpoint/:id ### Response #### Success Response (200) - **(No content)** - Indicates successful deletion. ``` -------------------------------- ### Define User Entity Request with Relations Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Extend EntityRequest to define request parameters for 'get' endpoints, supporting eager-loading of relations. Use @TransformRelations to automatically prune redundant relation paths. ```typescript import { EntityRequest } from '@ronas-it/rtkq-entity-api'; import { Expose } from 'class-transformer'; type UserRelation = 'posts' | 'posts.comments' | 'profile'; export class UserEntityRequest extends EntityRequest { // Inherited fields serialised to query params: // relations → ?with[]=posts&with[]=profile // withCount → ?with_count[]=posts // withAvg → ?with_avg[]=posts.rating } // Usage: usersApi.endpoints.get.initiate({ id: 42, params: new UserEntityRequest({ relations: ['posts', 'posts.comments', 'profile'] }), // TransformRelations automatically prunes 'posts' because 'posts.comments' is present // Result: ?with[]=posts.comments&with[]=profile }); ``` -------------------------------- ### Delete User with Cache Removal Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Deletes a user using `useDeleteMutation`. Upon successful deletion, the entity is automatically removed from all cached lists and get queries without triggering refetches. Ensure `usersApi` is imported. ```typescript import { usersApi } from './users-api'; function DeleteButton({ userId }: { userId: number }) { const [deleteUser, { isLoading }] = usersApi.useDeleteMutation(); const handleDelete = async () => { try { await deleteUser(userId).unwrap(); // Entity is removed from all cached search/get queries automatically. } catch (err) { console.error('Delete failed:', err.message); } }; return ; } ``` -------------------------------- ### setupRefetchListeners Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt A React Native-only utility that enhances RTK Query's `setupListeners` by integrating `AppState` (focus/blur) and `NetInfo` (online/offline) events. This ensures queries automatically refetch when the app regains foreground or network connectivity. ```APIDOC ## `setupRefetchListeners` React Native-only utility. Wraps `setupListeners` from RTK Query with `AppState` (focus/blur) and `NetInfo` (online/offline) events so queries automatically refetch when the app regains foreground or network connectivity. ```ts import { setupRefetchListeners } from '@ronas-it/rtkq-entity-api'; import { addEventListener, fetch } from '@react-native-community/netinfo'; import ReactNative, { AppState, Platform } from 'react-native'; import { useDispatch } from 'react-redux'; import { useEffect } from 'react'; function App() { const dispatch = useDispatch(); useEffect(() => { const unsubscribe = setupRefetchListeners( dispatch, { refetchOnFocus: true, refetchOnReconnect: true }, { addEventListener, fetch }, // NetInfo API { AppState, Platform }, // React Native refs ); return unsubscribe; // cleans up all listeners on unmount }, []); return ; } // Throws if called on web — use RTK Query's own setupListeners() for web apps. ``` ``` -------------------------------- ### Dispatch storeActions.init for App Initialization Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Dispatch `storeActions.init` when the root application component mounts to perform initial actions. This action can be used in side effects to trigger logic, such as fetching user settings. ```tsx import { store } from '@your-app/mobile/shared/data-access/store'; import { storeActions } from '@ronas-it/rtkq-entity-api'; import { ReactElement } from 'react'; function App(): ReactElement { const dispatch = useDispatch(); useEffect(() => { dispatch(storeActions.init()); }, []); ... } function Root(): ReactElement { return ( ); } ``` ```typescript userSettingsListenerMiddleware.startListening({ actionCreator: storeActions.init, effect: async (_, { dispatch }) => { const language = await appStorageService.language.get(); language && dispatch(userSettingsActions.setSystemLanguage(language as LanguageCode)); }, }); ``` -------------------------------- ### Initialize Redux Store with storeActions.init Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Dispatch this action at root component mount to trigger application startup side-effects. Use it within listener middleware to handle initial data loading. ```typescript import { storeActions } from '@ronas-it/rtkq-entity-api'; import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { userSettingsListenerMiddleware } from './listeners'; // Root App component: function App() { const dispatch = useDispatch(); useEffect(() => { dispatch(storeActions.init()); }, []); return ; } // Listener setup: userSettingsListenerMiddleware.startListening({ actionCreator: storeActions.init, effect: async (_, { dispatch }) => { const lang = await storage.get('language'); if (lang) dispatch(userSettingsActions.setLanguage(lang)); }, }); ``` -------------------------------- ### Bump Version and Create Tag Source: https://github.com/ronasit/rtkq-entity-api/blob/main/CONTRIBUTING.md Use npm to increment the version number in package.json and automatically create a Git commit and tag. ```bash npm version {patch|minor|major} ``` -------------------------------- ### storeActions.init Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt A Redux action to be dispatched at the root component mount. It serves as a trigger for application startup side-effects like loading settings, language, and tokens. ```APIDOC ## `storeActions.init` A Redux action dispatched at the root component mount. Use it as a trigger in listener middleware to run application startup side-effects (loading settings, language, tokens, etc.). ```ts import { storeActions } from '@ronas-it/rtkq-entity-api'; import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { userSettingsListenerMiddleware } from './listeners'; // Root App component: function App() { const dispatch = useDispatch(); useEffect(() => { dispatch(storeActions.init()); }, []); return ; } // Listener setup: userSettingsListenerMiddleware.startListening({ actionCreator: storeActions.init, effect: async (_, { dispatch }) => { const lang = await storage.get('language'); if (lang) dispatch(userSettingsActions.setLanguage(lang)); }, }); ``` ``` -------------------------------- ### Create Axios Base Query for API Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Set up a custom Axios base query for your API configuration. This involves creating an Axios instance with a base URL and then using it with the `createAxiosBaseQuery` utility. ```ts import axios from 'axios'; import { createApiCreator, createAxiosBaseQuery } from '@ronas-it/rtkq-entity-api'; const axiosBaseQuery = createAxiosBaseQuery({ getHttpClient: () => axios.create({ baseURL: 'https://your-api-url.com' }), }); export const createAppApi = createApiCreator({ baseQuery: axiosBaseQuery, }); ``` -------------------------------- ### createStoreInitializer / AppStateFromRootReducer Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Factory for `initStore()` function, configuring the Redux store with custom middleware and enhancers. `AppStateFromRootReducer` derives the `AppState` type from the root reducer map. ```APIDOC ## `createStoreInitializer` / `AppStateFromRootReducer` Factory that produces a reusable `initStore()` function. Configures the Redux store with `serializableCheck: false` (needed for Luxon `DateTime` values), custom middleware, and enhancers. `AppStateFromRootReducer` derives the `AppState` type from the root reducer map. ```ts import { createStoreInitializer, AppStateFromRootReducer } from '@ronas-it/rtkq-entity-api'; import { combineReducers, Reducer } from '@reduxjs/toolkit'; import { usersApi } from './users-api'; import { authApi } from './auth-api'; const rootReducer = { [usersApi.reducerPath]: usersApi.reducer, [authApi.reducerPath]: authApi.reducer, }; export type AppState = AppStateFromRootReducer; const initStore = createStoreInitializer({ rootReducer: combineReducers(rootReducer) as Reducer, middlewares: [usersApi.middleware, authApi.middleware], enhancers: [], // e.g., Reactotron enhancer in development }); export const store = initStore(); // Pass context for SSR: initStore({ req, res }) ``` ``` -------------------------------- ### Imperatively Fetch Entity and Upsert Cache Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Demonstrates using `usersApi.util.fetchEntity` within an `onQueryStarted` handler to fetch fresh data and then `usersApi.util.upsertQueryData` to update the cache. This is useful for manual cache management after mutations. Requires `createEntityApi` and `injectEndpoints`. ```typescript export const usersApi = createEntityApi({ ... }); // Inside a custom mutation's onQueryStarted: const extendedApi = usersApi.injectEndpoints({ endpoints: (builder) => ({ assignRole: builder.mutation({ query: ({ userId, roleId }) => ({ method: 'post', url: `/users/${userId}/roles/${roleId}` }), async onQueryStarted({ userId }, { dispatch, queryFulfilled }) { await queryFulfilled; // Refetch fresh entity data then upsert into the 'get' cache: const freshUser = await usersApi.util.fetchEntity(userId, {}, { dispatch }); usersApi.util.upsertQueryData('get', { id: userId }, freshUser); }, }), }), }); ``` -------------------------------- ### Push Changes and Tags Source: https://github.com/ronasit/rtkq-entity-api/blob/main/CONTRIBUTING.md After bumping the version, push the commit and associated tags to the remote repository. ```bash git push && git push --tags ``` -------------------------------- ### Run Code Checks Source: https://github.com/ronasit/rtkq-entity-api/blob/main/CONTRIBUTING.md Execute linting and testing scripts to ensure code quality and correctness before committing. ```bash lint ``` ```bash test ``` -------------------------------- ### Initialize Redux Store with createStoreInitializer Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Use `createStoreInitializer` to create a Redux store initializer function. It accepts root reducer, middlewares, and enhancers. The `AppStateFromRootReducer` helper type is provided for defining the application's state type. ```typescript export type AppState = AppStateFromRootReducer; const rootReducer = { [authApi.reducerPath]: authApi.reducer, }; const middlewares = [authApi.middleware]; const initStore = createStoreInitializer({ rootReducer: rootReducer as unknown as Reducer, middlewares, enhancers: [...reactotronEnhancer], }); export const store = initStore(); ``` -------------------------------- ### createAxiosBaseQuery Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Creates an RTK Query-compatible `baseQuery` function using Axios. It allows for custom Axios instance creation and header preparation, such as injecting authentication tokens. ```APIDOC ## createAxiosBaseQuery Creates an RTK Query-compatible `baseQuery` function backed by Axios. Accepts a factory for the Axios instance and an optional async `prepareHeaders` hook for injecting authentication tokens or other headers. ### Usage ```ts import axios from 'axios'; import { createAxiosBaseQuery } from '@ronas-it/rtkq-entity-api'; export const axiosBaseQuery = createAxiosBaseQuery({ getHttpClient: (api) => { // api.extra is the thunk extra argument (e.g., Next.js request context) return axios.create({ baseURL: 'https://api.example.com/v1' }); }, prepareHeaders: async (api) => { // Retrieve token from Redux state const token = (api.getState() as any).auth.accessToken; return token ? { Authorization: `Bearer ${token}` } : {}; }, }); // Errors are normalised: { error: { code: '422', message: 'Validation failed', data: {...} } } ``` ``` -------------------------------- ### Search Users with Pagination Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Fetches a paginated list of users using the `useSearchQuery` hook. It constructs a `UserSearchRequest` with pagination and query parameters. Ensure `UserSearchRequest` and `usersApi` are imported. ```typescript import { usersApi } from './users-api'; import { UserSearchRequest } from './models/requests'; function UserList() { const [page, setPage] = useState(1); const { data, isLoading } = usersApi.useSearchQuery( new UserSearchRequest({ page, perPage: 20, orderBy: 'name', query: 'alice' }), // → GET /users?page=1&per_page=20&order_by=name&query=alice ); return ( <> {data?.data.map((user) => )} ); } ``` -------------------------------- ### Create API Creator with Shared Base Query Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Returns a pre-configured `createApi` factory that enforces a shared `baseQuery`, tag types, or middleware across multiple entity APIs, reducing boilerplate. ```typescript import { createApiCreator, createAxiosBaseQuery } from '@ronas-it/rtkq-entity-api'; import axios from 'axios'; const axiosBaseQuery = createAxiosBaseQuery({ getHttpClient: () => axios.create({ baseURL: 'https://api.example.com' }), }); export const createAppApi = createApiCreator({ baseQuery: axiosBaseQuery, // Any other shared createApi options (tagTypes, middleware, etc.) }); // Later, createEntityApi can consume createAppApi instead of supplying its own baseQuery: // createEntityApi({ baseApiCreator: createAppApi, ... }) ``` -------------------------------- ### Create Axios Base Query for RTK Query Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Creates a baseQuery function for RTK Query using Axios. It accepts an Axios instance factory and an optional `prepareHeaders` hook for dynamic header injection, such as authentication tokens. ```typescript import axios from 'axios'; import { createAxiosBaseQuery } from '@ronas-it/rtkq-entity-api'; export const axiosBaseQuery = createAxiosBaseQuery({ getHttpClient: (api) => { // api.extra is the thunk extra argument (e.g., Next.js request context) return axios.create({ baseURL: 'https://api.example.com/v1' }); }, prepareHeaders: async (api) => { // Retrieve token from Redux state const token = (api.getState() as any).auth.accessToken; return token ? { Authorization: `Bearer ${token}` } : {}; }, }); // Errors are normalised: { error: { code: '422', message: 'Validation failed', data: {...} } } ``` -------------------------------- ### Endpoint: `create` Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Performs a `POST` request to the base endpoint for creating a new entity. It supports partial entity objects or `FormData` and handles serialization/deserialization using class-transformer decorators. ```APIDOC ## Endpoint: `create` Performs `POST /baseEndpoint`. Accepts a `Partial` or `FormData`. Serialises via `instanceToPlain` (respecting `@Expose` and `@Transform` decorators) and deserialises the response back to a typed entity instance. ### Usage ```ts import { usersApi } from './users-api'; function CreateUserForm() { const [createUser, { isLoading, error }] = usersApi.useCreateMutation(); const handleSubmit = async () => { try { const newUser = await createUser({ name: 'Alice', phoneNumber: '+1-555-0100', // phoneNumber is serialised as phone_number in the request body }).unwrap(); console.log('Created:', newUser.id, newUser.createdAt.toISO()); } catch (err) { console.error('Create failed:', err.message); } }; // File uploads also supported: pass FormData directly or mix File values in the partial. } ``` ``` -------------------------------- ### Generate Entity API with createEntityApi Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Generate your entity API using `createEntityApi`. Specify mandatory parameters like `entityName`, `entityConstructor`, and `baseEndpoint`. Optional parameters include `baseApiCreator`, `omitEndpoints`, `entityGetRequestConstructor`, and `entitySearchRequestConstructor`. ```ts import { createEntityApi } from '@ronas-it/rtkq-entity-api'; import { createAppApi } from 'your-project/utils'; import { User, UserEntityRequest, UserSearchRequest } from 'your-project/models'; export const usersApi = createEntityApi({ // Mandatory params entityName: 'user', // An entity name. Must by unique entityConstructor: User, // The entity model class constructor defined above baseEndpoint: '/users', // Endpoint, relative to base URL configured in the API creator // Optional params baseApiCreator: createAppApi, // The APIs creator from above that shares configuration for new APIs omitEndpoints: ['create', 'update', 'delete'], // Array to specify unimplemented endpoints entityGetRequestConstructor: UserEntityRequest // Request constructor for 'get' endpoint. Defaults to EntityRequest entitySearchRequestConstructor: UserSearchRequest, // Request constructor for 'search' endpoint. Defaults to PaginationRequest }); ``` -------------------------------- ### api.util.handleEntityUpdate Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt High-level helper for `onQueryStarted` that wraps `patchEntityQueries`. Supports optimistic updates with automatic rollback on error. ```APIDOC ## `api.util.handleEntityUpdate` High-level helper for `onQueryStarted` that wraps `patchEntityQueries`. Pass `optimistic: true` for immediate UI update with automatic rollback on error; default is pessimistic (waits for server response). ```ts const usersApi = createEntityApi({ ... }); const extApi = usersApi.injectEndpoints({ endpoints: (builder) => ({ deactivate: builder.mutation({ query: (id) => ({ method: 'put', url: `/users/${id}/deactivate` }), async onQueryStarted(id, apiLifecycle) { // Optimistic: immediately show user as inactive; roll back if request fails await usersApi.util.handleEntityUpdate( { id, isActive: false }, apiLifecycle, { optimistic: true }, ); }, }), }), }); ``` ``` -------------------------------- ### Handle Entity Updates with RTKQ Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md The `handleEntityUpdate` utility, which wraps `patchEntityQueries`, simplifies optimistic or pessimistic entity updates in `onQueryStarted` for tag-connected queries. ```typescript markAsFavorite: builder.mutation({ query: (id) => ({ method: 'put', url: `items/${id}/favorite` }), async onQueryStarted(id, apiLifecycle}) { // Perform optimistic entity update: await someItemApi.util.handleEntityUpdate({ id, isFavorite: true }, apiLifecycle, { optimistic: true }); // Or perform pessimistic entity update for specific tags: await someItemApi.util.handleEntityUpdate({ id, isFavorite: true }, apiLifecycle); } }) ``` -------------------------------- ### Handle Entity Updates with handleEntityUpdate Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt A high-level helper for `onQueryStarted` that simplifies entity updates. Pass `optimistic: true` for immediate UI updates with automatic rollback on error. ```typescript const usersApi = createEntityApi({ ... }); const extApi = usersApi.injectEndpoints({ endpoints: (builder) => ({ deactivate: builder.mutation({ query: (id) => ({ method: 'put', url: `/users/${id}/deactivate` }), async onQueryStarted(id, apiLifecycle) { // Optimistic: immediately show user as inactive; roll back if request fails await usersApi.util.handleEntityUpdate( { id, isActive: false }, apiLifecycle, { optimistic: true }, ); }, }), }), }); ``` -------------------------------- ### Handle Entity Deletes with RTKQ Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Use `handleEntityDelete`, internally using `clearEntityQueries`, for optimistic or pessimistic entity deletion from search-like queries connected by tags within `onQueryStarted`. ```typescript removeFromFavorite: builder.mutation({ query: (id) => ({ method: 'delete', url: `items/${id}/favorite` }), async onQueryStarted(id, apiLifecycle}) { // Perform optimistic entity delete: await someItemApi.util.handleEntityDelete(arg, apiLifecycle, { optimistic: true }); // Perform delete pessimistically for specific tags: await someItemApi.util.handleEntityDelete(arg, apiLifecycle, { tags: [{ type: 'item', id: 'favorites' }] }); } }) ``` -------------------------------- ### Create Endpoint Mutation Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Performs a `POST` request to the `baseEndpoint` for creating a new entity. Accepts a partial entity or `FormData`, serializes using `instanceToPlain`, and deserializes the response to a typed entity. ```typescript import { usersApi } from './users-api'; function CreateUserForm() { const [createUser, { isLoading, error }] = usersApi.useCreateMutation(); const handleSubmit = async () => { try { const newUser = await createUser({ name: 'Alice', phoneNumber: '+1-555-0100', // phoneNumber is serialised as phone_number in the request body }).unwrap(); console.log('Created:', newUser.id, newUser.createdAt.toISO()); } catch (err) { console.error('Create failed:', err.message); } }; // File uploads also supported: pass FormData directly or mix File values in the partial. ``` -------------------------------- ### Infinite Scrolling User List Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Implements infinite scrolling for a user list using RTK Query's `useSearchPaginatedInfiniteQuery`. This hook provides `flatData` for a merged list and `isRefetching` status. Ensure `UserSearchRequest` and `usersApi` are imported. ```typescript import { usersApi } from './users-api'; import { UserSearchRequest } from './models/requests'; function InfiniteUserList() { const { flatData, data, isFetching, isRefetching, fetchNextPage, hasNextPage, } = usersApi.useSearchPaginatedInfiniteQuery( new UserSearchRequest({ perPage: 20 }), ); return ( String(u.id)} renderItem={({ item }) => } onEndReached={() => !isFetching && fetchNextPage()} /> ); } ``` -------------------------------- ### createApiCreator Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Returns a pre-configured `createApi` factory. This factory ensures all APIs created with it share a common `baseQuery`, tag types, or middleware, reducing boilerplate across multiple entity APIs. ```APIDOC ## createApiCreator Returns a pre-configured `createApi` factory that stamps every API created with it with a shared `baseQuery`, tag types, or middleware — eliminating repeated boilerplate across multiple entity APIs. ### Usage ```ts import { createApiCreator, createAxiosBaseQuery } from '@ronas-it/rtkq-entity-api'; import axios from 'axios'; const axiosBaseQuery = createAxiosBaseQuery({ getHttpClient: () => axios.create({ baseURL: 'https://api.example.com' }), }); export const createAppApi = createApiCreator({ baseQuery: axiosBaseQuery, // Any other shared createApi options (tagTypes, middleware, etc.) }); // Later, createEntityApi can consume createAppApi instead of supplying its own baseQuery: // createEntityApi({ baseApiCreator: createAppApi, ... }) ``` ``` -------------------------------- ### api.util.handleEntityDelete Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt High-level helper for `onQueryStarted` that wraps `clearEntityQueries`. Supports optimistic deletion with rollback on failure. ```APIDOC ## `api.util.handleEntityDelete` High-level helper for `onQueryStarted` that wraps `clearEntityQueries`. Supports optimistic deletion with rollback on failure. ```ts const extApi = usersApi.injectEndpoints({ endpoints: (builder) => ({ banUser: builder.mutation({ query: (id) => ({ method: 'post', url: `/users/${id}/ban` }), async onQueryStarted(id, apiLifecycle) { // Pessimistic: wait for ban to succeed, then remove from all lists await usersApi.util.handleEntityDelete(id, apiLifecycle, { optimistic: false }); }, }), }), }); ``` ``` -------------------------------- ### Create Entity API with CRUD Endpoints Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Generates a fully-typed RTK Query API with standard CRUD endpoints and extended hooks. It can optionally share a `baseQuery` via `baseApiCreator`. ```typescript import { createEntityApi } from '@ronas-it/rtkq-entity-api'; import { createAppApi } from './api-creator'; import { User } from './models/user'; import { UserEntityRequest, UserSearchRequest } from './models/requests'; export const usersApi = createEntityApi({ entityName: 'user', // unique Redux reducer path + RTK Query tag type baseEndpoint: '/users', baseApiCreator: createAppApi, // shares baseQuery; alternatively pass `baseQuery` directly entityConstructor: User, entitySearchRequestConstructor: UserSearchRequest, entityGetRequestConstructor: UserEntityRequest, omitEndpoints: ['delete'], // remove endpoints your API doesn't implement getEntityId: (user) => user.id, // default: (item) => item.id }); // Auto-generated hooks: // usersApi.useCreateMutation() // usersApi.useGetQuery({ id: 1 }) // usersApi.useSearchQuery(new UserSearchRequest({ page: 1 })) // usersApi.useSearchPaginatedInfiniteQuery(new UserSearchRequest({})) // usersApi.useUpdateMutation() ``` -------------------------------- ### Handle Entity Deletions with handleEntityDelete Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt A high-level helper for `onQueryStarted` that wraps `clearEntityQueries`. It supports optimistic deletion with rollback on failure. ```typescript const extApi = usersApi.injectEndpoints({ endpoints: (builder) => ({ banUser: builder.mutation({ query: (id) => ({ method: 'post', url: `/users/${id}/ban` }), async onQueryStarted(id, apiLifecycle) { // Pessimistic: wait for ban to succeed, then remove from all lists await usersApi.util.handleEntityDelete(id, apiLifecycle, { optimistic: false }); }, }), }), }); ``` -------------------------------- ### Define User Entity Model Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Define your entity class by extending `BaseEntity`. Use decorators like `@Expose` for mapping API fields to class properties. Ensure a constructor that accepts partial model data. ```ts import { BaseEntity } from '@ronas-it/rtkq-entity-api'; export class User extends BaseEntity { name: string; @Expose({ name: 'phone_number' }) // APIs support of class-trasformer decorators phoneNumber: string; constructor(model: Partial) { super(model); Object.assign(this, model); } } ``` -------------------------------- ### handleEntityUpdate Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Uses `patchEntityQueries` internally and is intended for use in the `onQueryStarted` callback to perform optimistic/pessimistic updates of entity data in queries connected by tags. ```APIDOC ## handleEntityUpdate ### Description Uses `patchEntityQueries` internally and is intended for use in the `onQueryStarted` callback to perform optimistic/pessimistic update of entity data in queries connected by tags. ### Parameters #### Arguments - **entityData** (object) - Required - The data to update the entity with. - **apiLifecycle** (object) - Required - The API lifecycle object. - **options** (object) - Optional - Configuration options. - **optimistic** (boolean) - Optional - If true, performs an optimistic update. Defaults to false. - **tags** (Array) - Optional - Optionally, pass custom tags of queries to update. ``` -------------------------------- ### Search Paginated Infinite Query Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Uses RTK Query's `infiniteQuery` to accumulate pages of search results. The `useSearchPaginatedInfiniteQuery` hook wraps the built-in RTK Query infinite hook and provides `flatData` (all pages flattened) and an `isRefetching` flag. ```APIDOC ## Use Search Paginated Infinite Query ### Description Uses RTK Query's `infiniteQuery` to accumulate pages of search results. This hook provides `flatData` (all pages flattened) and an `isRefetching` flag. ### Method GET ### Endpoint /baseEndpoint ### Query Parameters - **perPage** (number) - Required - The number of items per page. ### Response #### Success Response (200) - **flatData** (array) - All pages merged into one array of entity objects. - **data** (object) - Raw paged structure containing all pages. - **isFetching** (boolean) - Indicates if a fetch is currently in progress. - **isRefetching** (boolean) - Indicates if a refetch is currently in progress. - **fetchNextPage** (function) - Function to fetch the next page of results. - **hasNextPage** (boolean) - Indicates if there is a next page available. ``` -------------------------------- ### Prepare Server-Side Request Headers with Axios Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Extracts the `Cookie` header from server-side request context and forwards it in Axios requests. Enables session-based authentication in SSR environments. ```typescript import { createAxiosBaseQuery, prepareServerSideRequestHeaders } from '@ronas-it/rtkq-entity-api'; import axios from 'axios'; export const axiosBaseQuery = createAxiosBaseQuery({ getHttpClient: () => axios.create({ baseURL: process.env.API_URL }), prepareHeaders: (api) => { // api.extra is the context passed as thunk extraArgument (e.g., { req, res }) return prepareServerSideRequestHeaders({ extra: api.extra }); // On server: returns { Cookie: 'session=abc123; ...' } // On browser: returns {} }, }); ``` -------------------------------- ### Define a User Entity with BaseEntity Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Extend BaseEntity to define your API entity. Use @Expose to map snake_case API fields to camelCase properties and ensure automatic ISO ↔ Luxon DateTime conversion for date fields. ```typescript import { BaseEntity } from '@ronas-it/rtkq-entity-api'; import { Expose } from 'class-transformer'; export class User extends BaseEntity { @Expose() name: string; @Expose({ name: 'phone_number' }) // maps snake_case API field to camelCase property phoneNumber: string; @Expose({ name: 'avatar_url' }) avatarUrl?: string; constructor(model: Partial) { super(model); Object.assign(this, model); } } // After deserialization, `user.createdAt` is a Luxon DateTime, `user.id` is a number. ``` -------------------------------- ### handleEntityDelete Source: https://github.com/ronasit/rtkq-entity-api/blob/main/README.md Uses `clearEntityQueries` internally and is intended for use in the `onQueryStarted` callback to perform optimistic/pessimistic entity delete from `search`-like queries connected by tags. ```APIDOC ## handleEntityDelete ### Description Uses `clearEntityQueries` internally and is intended for use in the `onQueryStarted` callback to perform optimistic/pessimistic entity delete from `search`-like queries connected by tags. ### Parameters #### Arguments - **entityId** (string | number) - Required - The ID of the entity to delete. - **apiLifecycle** (object) - Required - The API lifecycle object. - **options** (object) - Optional - Configuration options. - **optimistic** (boolean) - Optional - If true, performs an optimistic delete. Defaults to false. - **tags** (Array) - Optional - Optionally, pass custom tags of queries to delete from. ``` -------------------------------- ### createEntityApi Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt The primary factory function for generating typed RTK Query APIs. It includes standard CRUD endpoints and associated hooks, with options for customization like omitting endpoints or providing custom ID getters. ```APIDOC ## createEntityApi The primary factory function. Generates a fully-typed RTK Query API with six standard CRUD endpoints (`create`, `get`, `search`, `searchInfinite` [deprecated], `searchPaginated`, `update`, `delete`) plus extended hooks and cache utilities attached to `api.util`. ### Usage ```ts import { createEntityApi } from '@ronas-it/rtkq-entity-api'; import { createAppApi } from './api-creator'; import { User } from './models/user'; import { UserEntityRequest, UserSearchRequest } from './models/requests'; export const usersApi = createEntityApi({ entityName: 'user', // unique Redux reducer path + RTK Query tag type baseEndpoint: '/users', baseApiCreator: createAppApi, // shares baseQuery; alternatively pass `baseQuery` directly entityConstructor: User, entitySearchRequestConstructor: UserSearchRequest, entityGetRequestConstructor: UserEntityRequest, omitEndpoints: ['delete'], // remove endpoints your API doesn't implement getEntityId: (user) => user.id, // default: (item) => item.id }); // Auto-generated hooks: // usersApi.useCreateMutation() // usersApi.useGetQuery({ id: 1 }) // usersApi.useSearchQuery(new UserSearchRequest({ page: 1 })) // usersApi.useSearchPaginatedInfiniteQuery(new UserSearchRequest({})) // usersApi.useUpdateMutation() ``` ``` -------------------------------- ### Define User Search Request with Pagination Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt Extend PaginationRequest to include pagination, search, ordering, and custom boolean query parameters. This is used as the default request type for search endpoints. ```typescript import { PaginationRequest } from '@ronas-it/rtkq-entity-api'; import { Expose } from 'class-transformer'; type UserOrderBy = 'name' | 'created_at'; export class UserSearchRequest extends PaginationRequest { @Expose({ name: 'role_id' }) roleId?: number; constructor(request: Partial) { super(request); Object.assign(this, request); } } // Serialises to: GET /users?query=john&page=2&per_page=15&order_by=name&desc=0&role_id=3 const params = new UserSearchRequest({ query: 'john', page: 2, perPage: 15, orderBy: 'name', desc: false, roleId: 3 }); ``` -------------------------------- ### @TransformDate Decorator Source: https://context7.com/ronasit/rtkq-entity-api/llms.txt A class-transformer decorator that maps ISO date strings from the API to Luxon `DateTime` objects and vice versa. It supports date-only mode and custom format strings for flexible date handling. ```APIDOC ## `@TransformDate` Class-transformer decorator that maps ISO date strings from the API to Luxon `DateTime` objects and back. Supports date-only mode and custom format strings. ```ts import { BaseEntity, TransformDate } from '@ronas-it/rtkq-entity-api'; import { Expose, Type } from 'class-transformer'; import { DateTime } from 'luxon'; export class Event extends BaseEntity { @Expose({ name: 'event_date' }) @TransformDate({ dateOnly: true }) // serialises as 'YYYY-MM-DD' eventDate: DateTime; @Expose({ name: 'scheduled_at' }) @TransformDate({ format: 'dd/MM/yyyy' }) // custom Luxon format string scheduledAt: DateTime; constructor(model: Partial) { super(model); Object.assign(this, model); } } // API: { "event_date": "2025-06-15", "scheduled_at": "15/06/2025" } // JS: event.eventDate.toISODate() === '2025-06-15' ``` ```