@krymskyimaksym/react-api-client 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,409 @@
1
+ import { useState, useRef, useMemo, useCallback, useEffect } from 'react';
2
+
3
+ // src/config.ts
4
+ var globalConfig = null;
5
+ function configureApiClient(config) {
6
+ globalConfig = config;
7
+ }
8
+ function getConfig() {
9
+ if (!globalConfig) {
10
+ throw new Error(
11
+ "API client is not configured. Call configureApiClient() first."
12
+ );
13
+ }
14
+ return globalConfig;
15
+ }
16
+ function isConfigured() {
17
+ return globalConfig !== null;
18
+ }
19
+
20
+ // src/utils.ts
21
+ function buildEndpoint(endpoint, params) {
22
+ if (typeof endpoint === "function" && typeof params !== "undefined") {
23
+ return endpoint(params);
24
+ }
25
+ return endpoint;
26
+ }
27
+ async function executeRequest(endpoint, fetchConfig, params) {
28
+ const url = buildEndpoint(endpoint, params);
29
+ const config = getConfig();
30
+ try {
31
+ const requestConfig = { ...fetchConfig };
32
+ let response;
33
+ if (requestConfig.method?.toUpperCase() === "GET" || !requestConfig.method) {
34
+ response = await config.httpClient.get(url, {
35
+ params: {
36
+ ...requestConfig.requestParams,
37
+ ...params
38
+ }
39
+ });
40
+ } else {
41
+ response = await config.httpClient.request(url, {
42
+ method: requestConfig.method,
43
+ data: {
44
+ ...requestConfig.requestParams,
45
+ ...params
46
+ }
47
+ });
48
+ }
49
+ return { ...response, status: true };
50
+ } catch (e) {
51
+ const error = e;
52
+ if (error.response?.status === 401 && config.onUnauthorized) {
53
+ await config.onUnauthorized();
54
+ }
55
+ if (error.response?.data) {
56
+ return {
57
+ ...error.response.data,
58
+ status: false
59
+ };
60
+ }
61
+ return {
62
+ status: false,
63
+ message: error instanceof Error ? error.message : "Request error"
64
+ };
65
+ }
66
+ }
67
+ function handleResponse(result, onSuccess, onError) {
68
+ if (result.status && onSuccess) {
69
+ onSuccess(result);
70
+ } else if (!result.status && onError) {
71
+ const err = new Error(result.message ?? "Request failed");
72
+ onError(err);
73
+ }
74
+ }
75
+ function createUseFetch(endpoint, fetchConfig) {
76
+ return (params, options = {}) => {
77
+ const {
78
+ enabled = true,
79
+ refetchOnMount = true,
80
+ onSuccess,
81
+ onError
82
+ } = options;
83
+ const [data, setData] = useState(null);
84
+ const [isLoading, setIsLoading] = useState(enabled && refetchOnMount);
85
+ const [isRefetching, setIsRefetching] = useState(false);
86
+ const [error, setError] = useState(null);
87
+ const isMountedRef = useRef(true);
88
+ const serializedParams = useMemo(
89
+ () => params ? JSON.stringify(params) : null,
90
+ [params]
91
+ );
92
+ const fetchData = useCallback(
93
+ async (isRefetch = false) => {
94
+ if (!enabled) return;
95
+ try {
96
+ if (isRefetch) {
97
+ setIsRefetching(true);
98
+ } else {
99
+ setIsLoading(true);
100
+ }
101
+ setError(null);
102
+ const parsedParams = serializedParams ? JSON.parse(serializedParams) : void 0;
103
+ const result = await executeRequest(endpoint, fetchConfig, parsedParams);
104
+ if (isMountedRef.current) {
105
+ setData(result);
106
+ handleResponse(
107
+ result,
108
+ onSuccess,
109
+ onError
110
+ );
111
+ }
112
+ } catch (err) {
113
+ const error2 = err;
114
+ if (isMountedRef.current) {
115
+ setError(error2);
116
+ if (onError) {
117
+ onError(error2);
118
+ }
119
+ }
120
+ } finally {
121
+ if (isMountedRef.current) {
122
+ if (isRefetch) {
123
+ setIsRefetching(false);
124
+ } else {
125
+ setIsLoading(false);
126
+ }
127
+ }
128
+ }
129
+ },
130
+ [enabled, serializedParams, onSuccess, onError]
131
+ );
132
+ const refetch = useCallback(async () => {
133
+ await fetchData(true);
134
+ }, [fetchData]);
135
+ useEffect(() => {
136
+ isMountedRef.current = true;
137
+ if (enabled && refetchOnMount) {
138
+ void fetchData(false);
139
+ }
140
+ return () => {
141
+ isMountedRef.current = false;
142
+ };
143
+ }, [enabled, refetchOnMount, fetchData]);
144
+ return {
145
+ data,
146
+ isLoading,
147
+ isRefetching,
148
+ error,
149
+ refetch
150
+ };
151
+ };
152
+ }
153
+ function createUseMutation(endpoint, fetchConfig) {
154
+ return (options = {}) => {
155
+ const { onMutate, onSuccess, onError, onSettled } = options;
156
+ const [data, setData] = useState(null);
157
+ const [error, setError] = useState(null);
158
+ const [isLoading, setIsLoading] = useState(false);
159
+ const [isSuccess, setIsSuccess] = useState(false);
160
+ const [isError, setIsError] = useState(false);
161
+ const reset = useCallback(() => {
162
+ setData(null);
163
+ setError(null);
164
+ setIsLoading(false);
165
+ setIsSuccess(false);
166
+ setIsError(false);
167
+ }, []);
168
+ const mutateAsync = useCallback(
169
+ async (variables) => {
170
+ setIsLoading(true);
171
+ setIsSuccess(false);
172
+ setIsError(false);
173
+ setError(null);
174
+ try {
175
+ if (onMutate) {
176
+ await onMutate(variables);
177
+ }
178
+ const result = await executeRequest(endpoint, fetchConfig, variables);
179
+ setData(result);
180
+ if (result.status) {
181
+ setIsSuccess(true);
182
+ if (onSuccess) {
183
+ await onSuccess(result, variables);
184
+ }
185
+ } else {
186
+ const err = new Error(result.message ?? "Mutation failed");
187
+ setIsError(true);
188
+ setError(err);
189
+ if (onError) {
190
+ await onError(err, variables);
191
+ }
192
+ }
193
+ if (onSettled) {
194
+ await onSettled(result, null, variables);
195
+ }
196
+ return result;
197
+ } catch (err) {
198
+ const error2 = err;
199
+ setError(error2);
200
+ setIsError(true);
201
+ setIsSuccess(false);
202
+ if (onError) {
203
+ await onError(error2, variables);
204
+ }
205
+ if (onSettled) {
206
+ await onSettled(null, error2, variables);
207
+ }
208
+ throw error2;
209
+ } finally {
210
+ setIsLoading(false);
211
+ }
212
+ },
213
+ [onMutate, onSuccess, onError, onSettled]
214
+ );
215
+ const mutateSync = useCallback(
216
+ (variables) => {
217
+ void mutateAsync(variables);
218
+ },
219
+ [mutateAsync]
220
+ );
221
+ return {
222
+ data,
223
+ error,
224
+ isLoading,
225
+ isSuccess,
226
+ isError,
227
+ mutate: mutateSync,
228
+ mutateAsync,
229
+ reset
230
+ };
231
+ };
232
+ }
233
+ function createUsePaginate(endpoint, fetchConfig, options) {
234
+ return (params, hookOptions = {}) => {
235
+ const {
236
+ enabled = true,
237
+ initialPage = 1,
238
+ initialLimit = 20,
239
+ onSuccess,
240
+ onError
241
+ } = hookOptions;
242
+ const [data, setData] = useState([]);
243
+ const [currentPage, setCurrentPage] = useState(initialPage);
244
+ const [totalPages, setTotalPages] = useState(null);
245
+ const [total, setTotal] = useState(null);
246
+ const [isLoading, setIsLoading] = useState(enabled);
247
+ const [isFetchingNextPage, setIsFetchingNextPage] = useState(false);
248
+ const [error, setError] = useState(null);
249
+ const isMountedRef = useRef(true);
250
+ const limit = initialLimit;
251
+ const dataExtractor = useMemo(
252
+ () => options?.dataExtractor || ((response) => response.data),
253
+ []
254
+ );
255
+ const totalExtractor = useMemo(
256
+ () => options?.totalExtractor || ((response) => response.total ?? 0),
257
+ []
258
+ );
259
+ const serializedParams = useMemo(
260
+ () => params ? JSON.stringify(params) : null,
261
+ [params]
262
+ );
263
+ const fetchPage = useCallback(
264
+ async (page, append = false) => {
265
+ if (!enabled) return;
266
+ try {
267
+ if (append) {
268
+ setIsFetchingNextPage(true);
269
+ } else {
270
+ setIsLoading(true);
271
+ }
272
+ setError(null);
273
+ const parsedParams = serializedParams ? JSON.parse(serializedParams) : {};
274
+ const requestParams = {
275
+ ...parsedParams,
276
+ page,
277
+ limit
278
+ };
279
+ const result = await executeRequest(endpoint, fetchConfig, requestParams);
280
+ if (isMountedRef.current) {
281
+ handleResponse(
282
+ result,
283
+ onSuccess,
284
+ onError
285
+ );
286
+ if (!result.status) {
287
+ const err = new Error(result.message ?? "Request failed");
288
+ setError(err);
289
+ return;
290
+ }
291
+ const newData = dataExtractor(result);
292
+ const totalCount = totalExtractor(result);
293
+ if (append) {
294
+ setData((prevData) => [...prevData, ...newData]);
295
+ } else {
296
+ setData(newData);
297
+ }
298
+ setCurrentPage(page);
299
+ setTotal(totalCount);
300
+ setTotalPages(Math.ceil(totalCount / limit));
301
+ }
302
+ } catch (err) {
303
+ const error2 = err;
304
+ if (isMountedRef.current) {
305
+ setError(error2);
306
+ if (onError) {
307
+ onError(error2);
308
+ }
309
+ }
310
+ } finally {
311
+ if (isMountedRef.current) {
312
+ if (append) {
313
+ setIsFetchingNextPage(false);
314
+ } else {
315
+ setIsLoading(false);
316
+ }
317
+ }
318
+ }
319
+ },
320
+ [
321
+ enabled,
322
+ serializedParams,
323
+ limit,
324
+ onSuccess,
325
+ onError,
326
+ dataExtractor,
327
+ totalExtractor
328
+ ]
329
+ );
330
+ const hasNextPage = totalPages !== null && currentPage < totalPages;
331
+ const hasPreviousPage = currentPage > 1;
332
+ const fetchNextPage = useCallback(async () => {
333
+ if (!hasNextPage) return;
334
+ await fetchPage(currentPage + 1, true);
335
+ }, [hasNextPage, currentPage, fetchPage]);
336
+ const fetchPreviousPage = useCallback(async () => {
337
+ if (!hasPreviousPage) return;
338
+ await fetchPage(currentPage - 1, false);
339
+ }, [hasPreviousPage, currentPage, fetchPage]);
340
+ const refetch = useCallback(async () => {
341
+ await fetchPage(currentPage, false);
342
+ }, [currentPage, fetchPage]);
343
+ const reset = useCallback(() => {
344
+ setData([]);
345
+ setCurrentPage(initialPage);
346
+ setTotalPages(null);
347
+ setTotal(null);
348
+ setError(null);
349
+ void fetchPage(initialPage, false);
350
+ }, [initialPage, fetchPage]);
351
+ useEffect(() => {
352
+ isMountedRef.current = true;
353
+ if (enabled) {
354
+ void fetchPage(initialPage, false);
355
+ }
356
+ return () => {
357
+ isMountedRef.current = false;
358
+ };
359
+ }, [enabled, serializedParams, fetchPage, initialPage]);
360
+ return {
361
+ data,
362
+ currentPage,
363
+ totalPages,
364
+ total,
365
+ hasNextPage,
366
+ hasPreviousPage,
367
+ isLoading,
368
+ isFetchingNextPage,
369
+ error,
370
+ fetchNextPage,
371
+ fetchPreviousPage,
372
+ refetch,
373
+ reset
374
+ };
375
+ };
376
+ }
377
+
378
+ // src/index.ts
379
+ function apiClient(endpoint, fetchConfig = {}) {
380
+ const fetch = async (params) => {
381
+ return executeRequest(
382
+ endpoint,
383
+ fetchConfig,
384
+ params
385
+ );
386
+ };
387
+ const useFetch = createUseFetch(endpoint, fetchConfig);
388
+ return { fetch, useFetch };
389
+ }
390
+ function apiMutation(endpoint, fetchConfig = {}) {
391
+ const mutate = async (params) => {
392
+ return executeRequest(
393
+ endpoint,
394
+ fetchConfig,
395
+ params
396
+ );
397
+ };
398
+ const useMutation = createUseMutation(endpoint, fetchConfig);
399
+ return { mutate, useMutation };
400
+ }
401
+ function apiPaginate(endpoint, fetchConfig = {}, options) {
402
+ const usePaginate = createUsePaginate(endpoint, fetchConfig, options);
403
+ return { usePaginate };
404
+ }
405
+ var index_default = apiClient;
406
+
407
+ export { apiMutation, apiPaginate, configureApiClient, index_default as default, getConfig, isConfigured };
408
+ //# sourceMappingURL=index.mjs.map
409
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../src/utils.ts","../src/hooks/use-fetch.ts","../src/hooks/use-mutation.ts","../src/hooks/use-paginate.ts","../src/index.ts"],"names":["error","useState","useCallback","useRef","useMemo","useEffect"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ApiClientConfig } from './types';\n\nlet globalConfig: ApiClientConfig | null = null;\n\n/**\n * Configure the API client globally\n * @param config - API client configuration\n *\n * @example\n * import { configureApiClient } from '@krymskyimaksym/react-api-client';\n * import { router } from 'expo-router';\n *\n * configureApiClient({\n * httpClient: myHttpClient,\n * onUnauthorized: () => router.replace('/login')\n * });\n */\nexport function configureApiClient(config: ApiClientConfig): void {\n globalConfig = config;\n}\n\n/**\n * Get the current global configuration\n * @throws Error if configuration is not set\n */\nexport function getConfig(): ApiClientConfig {\n if (!globalConfig) {\n throw new Error(\n 'API client is not configured. Call configureApiClient() first.',\n );\n }\n return globalConfig;\n}\n\n/**\n * Check if the API client is configured\n */\nexport function isConfigured(): boolean {\n return globalConfig !== null;\n}\n","import { getConfig } from './config';\n\nimport type { RequestConfig, ResponseWrapper } from './types';\n\n/**\n * Builds the endpoint URL from a string or function\n */\nexport function buildEndpoint<RequestParamsType>(\n endpoint: string | ((arg0: RequestParamsType) => string),\n params?: RequestParamsType,\n): string {\n if (typeof endpoint === 'function' && typeof params !== 'undefined') {\n return endpoint(params);\n }\n return endpoint as string;\n}\n\n/**\n * Executes an HTTP request with error handling and response wrapping\n */\nexport async function executeRequest<\n ResponseType,\n RequestParamsType,\n ErrorResponseType,\n>(\n endpoint: string | ((arg0: RequestParamsType) => string),\n fetchConfig: RequestConfig,\n params?: RequestParamsType,\n): Promise<ResponseWrapper<ResponseType, ErrorResponseType>> {\n type RT = ResponseWrapper<ResponseType, ErrorResponseType>;\n const url = buildEndpoint(endpoint, params);\n const config = getConfig();\n\n try {\n const requestConfig = { ...fetchConfig };\n let response: ResponseType;\n\n if (\n requestConfig.method?.toUpperCase() === 'GET' ||\n !requestConfig.method\n ) {\n response = await config.httpClient.get<ResponseType>(url, {\n params: {\n ...requestConfig.requestParams,\n ...(params as Record<string, string>),\n },\n });\n } else {\n response = await config.httpClient.request<ResponseType>(url, {\n method: requestConfig.method,\n data: {\n ...requestConfig.requestParams,\n ...(params as Record<string, unknown>),\n },\n });\n }\n\n return { ...response, status: true } as RT;\n } catch (e) {\n const error = e as {\n response?: { status: number; data: ErrorResponseType };\n };\n\n if (error.response?.status === 401 && config.onUnauthorized) {\n await config.onUnauthorized();\n }\n\n if (error.response?.data) {\n return {\n ...(error.response.data as Record<string, unknown>),\n status: false,\n } as unknown as RT;\n }\n\n return {\n status: false,\n message: error instanceof Error ? error.message : 'Request error',\n } as unknown as RT;\n }\n}\n\n/**\n * Handles response success and error callbacks\n */\nexport function handleResponse<T, E = unknown>(\n result: ResponseWrapper<T, E>,\n onSuccess?: (data: ResponseWrapper<T, E>) => void,\n onError?: (error: Error) => void,\n): void {\n if (result.status && onSuccess) {\n onSuccess(result);\n } else if (!result.status && onError) {\n const err = new Error(result.message ?? 'Request failed');\n onError(err);\n }\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { executeRequest, handleResponse } from '../utils';\n\nimport type {\n RequestConfig,\n ResponseWrapper,\n UseFetchOptions,\n UseFetchResult,\n} from '../types/index';\n\n/**\n * Hook for fetching data (GET requests)\n */\nexport function createUseFetch<\n ResponseType,\n RequestParamsType,\n ErrorResponseType,\n>(\n endpoint: string | ((arg0: RequestParamsType) => string),\n fetchConfig: RequestConfig,\n) {\n type RT = ResponseWrapper<ResponseType, ErrorResponseType>;\n\n return (\n params?: RequestParamsType,\n options: UseFetchOptions<RT> = {},\n ): UseFetchResult<RT> => {\n const {\n enabled = true,\n refetchOnMount = true,\n onSuccess,\n onError,\n } = options;\n\n const [data, setData] = useState<RT | null>(null);\n const [isLoading, setIsLoading] = useState(enabled && refetchOnMount);\n const [isRefetching, setIsRefetching] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const isMountedRef = useRef(true);\n\n // Serialize params to avoid unnecessary re-renders\n const serializedParams = useMemo(\n () => (params ? JSON.stringify(params) : null),\n [params],\n );\n\n const fetchData = useCallback(\n async (isRefetch = false) => {\n if (!enabled) return;\n\n try {\n if (isRefetch) {\n setIsRefetching(true);\n } else {\n setIsLoading(true);\n }\n setError(null);\n\n const parsedParams = serializedParams\n ? JSON.parse(serializedParams)\n : undefined;\n\n const result = await executeRequest<\n ResponseType,\n RequestParamsType,\n ErrorResponseType\n >(endpoint, fetchConfig, parsedParams);\n\n if (isMountedRef.current) {\n setData(result);\n handleResponse<ResponseType, ErrorResponseType>(\n result,\n onSuccess,\n onError,\n );\n }\n } catch (err) {\n const error = err as Error;\n if (isMountedRef.current) {\n setError(error);\n if (onError) {\n onError(error);\n }\n }\n } finally {\n if (isMountedRef.current) {\n if (isRefetch) {\n setIsRefetching(false);\n } else {\n setIsLoading(false);\n }\n }\n }\n },\n [enabled, serializedParams, onSuccess, onError],\n );\n\n const refetch = useCallback(async () => {\n await fetchData(true);\n }, [fetchData]);\n\n useEffect(() => {\n isMountedRef.current = true;\n\n if (enabled && refetchOnMount) {\n void fetchData(false);\n }\n\n return () => {\n isMountedRef.current = false;\n };\n }, [enabled, refetchOnMount, fetchData]);\n\n return {\n data,\n isLoading,\n isRefetching,\n error,\n refetch,\n };\n };\n}\n","import { useCallback, useState } from 'react';\n\nimport { executeRequest } from '../utils';\n\nimport type {\n RequestConfig,\n ResponseWrapper,\n UseMutationOptions,\n UseMutationResult,\n} from '../types/index';\n\n/**\n * Hook for mutations (POST/PUT/PATCH/DELETE requests)\n */\nexport function createUseMutation<\n ResponseType,\n RequestParamsType,\n ErrorResponseType,\n>(\n endpoint: string | ((arg0: RequestParamsType) => string),\n fetchConfig: RequestConfig,\n) {\n type RT = ResponseWrapper<ResponseType, ErrorResponseType>;\n\n return (\n options: UseMutationOptions<RT, RequestParamsType> = {},\n ): UseMutationResult<RT, RequestParamsType> => {\n const { onMutate, onSuccess, onError, onSettled } = options;\n\n const [data, setData] = useState<RT | null>(null);\n const [error, setError] = useState<Error | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [isSuccess, setIsSuccess] = useState(false);\n const [isError, setIsError] = useState(false);\n\n const reset = useCallback(() => {\n setData(null);\n setError(null);\n setIsLoading(false);\n setIsSuccess(false);\n setIsError(false);\n }, []);\n\n const mutateAsync = useCallback(\n async (variables: RequestParamsType): Promise<RT> => {\n setIsLoading(true);\n setIsSuccess(false);\n setIsError(false);\n setError(null);\n\n try {\n // onMutate callback\n if (onMutate) {\n await onMutate(variables);\n }\n\n const result = await executeRequest<\n ResponseType,\n RequestParamsType,\n ErrorResponseType\n >(endpoint, fetchConfig, variables);\n\n setData(result);\n\n if (result.status) {\n setIsSuccess(true);\n // onSuccess callback\n if (onSuccess) {\n await onSuccess(result, variables);\n }\n } else {\n const err = new Error(result.message ?? 'Mutation failed');\n setIsError(true);\n setError(err);\n // onError callback\n if (onError) {\n await onError(err, variables);\n }\n }\n\n // onSettled callback (always called)\n if (onSettled) {\n await onSettled(result, null, variables);\n }\n\n return result;\n } catch (err) {\n const error = err as Error;\n setError(error);\n setIsError(true);\n setIsSuccess(false);\n\n // onError callback\n if (onError) {\n await onError(error, variables);\n }\n\n // onSettled callback (always called)\n if (onSettled) {\n await onSettled(null, error, variables);\n }\n\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [onMutate, onSuccess, onError, onSettled],\n );\n\n const mutateSync = useCallback(\n (variables: RequestParamsType) => {\n void mutateAsync(variables);\n },\n [mutateAsync],\n );\n\n return {\n data,\n error,\n isLoading,\n isSuccess,\n isError,\n mutate: mutateSync,\n mutateAsync,\n reset,\n };\n };\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { executeRequest, handleResponse } from '../utils';\n\nimport type {\n RequestConfig,\n ResponseWrapper,\n UsePaginateOptions,\n UsePaginateResult,\n} from '../types/index';\n\n/**\n * Hook for paginated data fetching\n */\nexport function createUsePaginate<\n ResponseType extends { data: TData; total?: number; page?: number },\n TData extends unknown[],\n RequestParamsType,\n ErrorResponseType,\n>(\n endpoint: string | ((arg0: RequestParamsType) => string),\n fetchConfig: RequestConfig,\n options?: {\n dataExtractor?: (response: ResponseType) => TData;\n totalExtractor?: (response: ResponseType) => number;\n },\n) {\n return (\n params?: Omit<RequestParamsType, 'page' | 'limit'>,\n hookOptions: UsePaginateOptions<\n ResponseWrapper<ResponseType, ErrorResponseType>\n > = {},\n ): UsePaginateResult<TData> => {\n const {\n enabled = true,\n initialPage = 1,\n initialLimit = 20,\n onSuccess,\n onError,\n } = hookOptions;\n\n const [data, setData] = useState<TData>([] as unknown as TData);\n const [currentPage, setCurrentPage] = useState(initialPage);\n const [totalPages, setTotalPages] = useState<number | null>(null);\n const [total, setTotal] = useState<number | null>(null);\n const [isLoading, setIsLoading] = useState(enabled);\n const [isFetchingNextPage, setIsFetchingNextPage] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const isMountedRef = useRef(true);\n const limit = initialLimit;\n\n const dataExtractor = useMemo(\n () =>\n options?.dataExtractor || ((response: ResponseType) => response.data),\n [],\n );\n\n const totalExtractor = useMemo(\n () =>\n options?.totalExtractor ||\n ((response: ResponseType) => response.total ?? 0),\n [],\n );\n\n const serializedParams = useMemo(\n () => (params ? JSON.stringify(params) : null),\n [params],\n );\n\n const fetchPage = useCallback(\n async (page: number, append = false) => {\n if (!enabled) return;\n\n try {\n if (append) {\n setIsFetchingNextPage(true);\n } else {\n setIsLoading(true);\n }\n setError(null);\n\n const parsedParams = serializedParams\n ? JSON.parse(serializedParams)\n : {};\n const requestParams = {\n ...parsedParams,\n page,\n limit,\n } as RequestParamsType;\n\n const result = await executeRequest<\n ResponseType,\n RequestParamsType,\n ErrorResponseType\n >(endpoint, fetchConfig, requestParams);\n\n if (isMountedRef.current) {\n handleResponse<ResponseType, ErrorResponseType>(\n result,\n onSuccess,\n onError,\n );\n\n if (!result.status) {\n const err = new Error(result.message ?? 'Request failed');\n setError(err);\n return;\n }\n\n const newData = dataExtractor(result as ResponseType);\n const totalCount = totalExtractor(result as ResponseType);\n\n if (append) {\n setData(prevData => [...prevData, ...newData] as TData);\n } else {\n setData(newData);\n }\n\n setCurrentPage(page);\n setTotal(totalCount);\n setTotalPages(Math.ceil(totalCount / limit));\n }\n } catch (err) {\n const error = err as Error;\n if (isMountedRef.current) {\n setError(error);\n if (onError) {\n onError(error);\n }\n }\n } finally {\n if (isMountedRef.current) {\n if (append) {\n setIsFetchingNextPage(false);\n } else {\n setIsLoading(false);\n }\n }\n }\n },\n [\n enabled,\n serializedParams,\n limit,\n onSuccess,\n onError,\n dataExtractor,\n totalExtractor,\n ],\n );\n\n const hasNextPage = totalPages !== null && currentPage < totalPages;\n const hasPreviousPage = currentPage > 1;\n\n const fetchNextPage = useCallback(async () => {\n if (!hasNextPage) return;\n await fetchPage(currentPage + 1, true);\n }, [hasNextPage, currentPage, fetchPage]);\n\n const fetchPreviousPage = useCallback(async () => {\n if (!hasPreviousPage) return;\n await fetchPage(currentPage - 1, false);\n }, [hasPreviousPage, currentPage, fetchPage]);\n\n const refetch = useCallback(async () => {\n await fetchPage(currentPage, false);\n }, [currentPage, fetchPage]);\n\n const reset = useCallback(() => {\n setData([] as unknown as TData);\n setCurrentPage(initialPage);\n setTotalPages(null);\n setTotal(null);\n setError(null);\n void fetchPage(initialPage, false);\n }, [initialPage, fetchPage]);\n\n useEffect(() => {\n isMountedRef.current = true;\n\n if (enabled) {\n void fetchPage(initialPage, false);\n }\n\n return () => {\n isMountedRef.current = false;\n };\n }, [enabled, serializedParams, fetchPage, initialPage]);\n\n return {\n data,\n currentPage,\n totalPages,\n total,\n hasNextPage,\n hasPreviousPage,\n isLoading,\n isFetchingNextPage,\n error,\n fetchNextPage,\n fetchPreviousPage,\n refetch,\n reset,\n };\n };\n}\n","import { executeRequest } from './utils';\n\nimport { createUseFetch, createUseMutation, createUsePaginate } from './hooks';\n\nimport type {\n ApiClientReturn,\n ApiMutationReturn,\n ApiPaginateReturn,\n RequestConfig,\n ResponseWrapper,\n} from './types';\n\n/**\n * Creates an API client for GET requests\n * @param endpoint - URL string or function that generates URL from params\n * @param fetchConfig - Request configuration\n * @returns Object with fetch method and useFetch hook\n *\n * @example\n * const userApi = apiClient<User, { id: string }>('/api/users/:id')\n * const { data } = userApi.useFetch({ id: '123' })\n */\nfunction apiClient<\n ResponseType = void,\n RequestParamsType = void,\n ErrorResponseType = unknown,\n>(\n endpoint: string | ((arg0: RequestParamsType) => string),\n fetchConfig: RequestConfig = {},\n): ApiClientReturn<ResponseType, RequestParamsType, ErrorResponseType> {\n type RT = ResponseWrapper<ResponseType, ErrorResponseType>;\n\n const fetch = async (params?: RequestParamsType): Promise<RT> => {\n return executeRequest<ResponseType, RequestParamsType, ErrorResponseType>(\n endpoint,\n fetchConfig,\n params,\n );\n };\n\n const useFetch = createUseFetch<\n ResponseType,\n RequestParamsType,\n ErrorResponseType\n >(endpoint, fetchConfig);\n\n return { fetch, useFetch };\n}\n\n/**\n * Creates an API client for mutation requests (POST/PUT/PATCH/DELETE)\n * @param endpoint - URL string or function that generates URL from params\n * @param fetchConfig - Request configuration\n * @returns Object with mutate method and useMutation hook\n *\n * @example\n * const createUserApi = apiMutation<User, CreateUserRequest>('/api/users', { method: 'POST' })\n * const { mutate, isLoading } = createUserApi.useMutation()\n */\nfunction apiMutation<\n ResponseType = void,\n RequestParamsType = void,\n ErrorResponseType = unknown,\n>(\n endpoint: string | ((arg0: RequestParamsType) => string),\n fetchConfig: RequestConfig = {},\n): ApiMutationReturn<ResponseType, RequestParamsType, ErrorResponseType> {\n type RT = ResponseWrapper<ResponseType, ErrorResponseType>;\n\n const mutate = async (params?: RequestParamsType): Promise<RT> => {\n return executeRequest<ResponseType, RequestParamsType, ErrorResponseType>(\n endpoint,\n fetchConfig,\n params,\n );\n };\n\n const useMutation = createUseMutation<\n ResponseType,\n RequestParamsType,\n ErrorResponseType\n >(endpoint, fetchConfig);\n\n return { mutate, useMutation };\n}\n\n/**\n * Creates an API client for paginated requests\n * @param endpoint - URL string or function that generates URL from params\n * @param fetchConfig - Request configuration\n * @param options - Pagination options (data/total extractors)\n * @returns Object with usePaginate hook\n *\n * @example\n * const usersApi = apiPaginate<UsersResponse, User[], { search?: string }>('/api/users')\n * const { data, fetchNextPage, hasNextPage } = usersApi.usePaginate()\n */\nfunction apiPaginate<\n ResponseType extends { data: TData; total?: number; page?: number },\n TData extends unknown[],\n RequestParamsType = void,\n ErrorResponseType = unknown,\n>(\n endpoint: string | ((arg0: RequestParamsType) => string),\n fetchConfig: RequestConfig = {},\n options?: {\n dataExtractor?: (response: ResponseType) => TData;\n totalExtractor?: (response: ResponseType) => number;\n },\n): ApiPaginateReturn<\n ResponseType,\n RequestParamsType,\n TData,\n ErrorResponseType\n> {\n const usePaginate = createUsePaginate<\n ResponseType,\n TData,\n RequestParamsType,\n ErrorResponseType\n >(endpoint, fetchConfig, options);\n\n return { usePaginate };\n}\n\nexport default apiClient;\nexport { apiMutation, apiPaginate };\n\n// Export configuration\nexport { configureApiClient, getConfig, isConfigured } from './config';\n\n// Re-export types for convenience\nexport type {\n ResponseWrapper,\n UseFetchOptions,\n UseFetchResult,\n UseMutationOptions,\n UseMutationResult,\n UsePaginateOptions,\n UsePaginateResult,\n ApiClientConfig,\n IHttpClient,\n} from './types';\n"]}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@krymskyimaksym/react-api-client",
3
+ "version": "1.0.0",
4
+ "description": "A lightweight, type-safe API client for React and React Native with built-in hooks",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "dev": "tsup --watch",
23
+ "lint": "eslint src --ext .ts,.tsx",
24
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
25
+ "typecheck": "tsc --noEmit",
26
+ "test": "vitest",
27
+ "test:coverage": "vitest --coverage",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "keywords": [
31
+ "react",
32
+ "react-native",
33
+ "api-client",
34
+ "fetch",
35
+ "hooks",
36
+ "typescript",
37
+ "http",
38
+ "rest",
39
+ "pagination",
40
+ "mutation"
41
+ ],
42
+ "author": "Krymskyi Maksym <krimskiymaxim@gmail.com>",
43
+ "license": "MIT",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/krymskyimaksym/react-api-client.git"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/krymskyimaksym/react-api-client/issues"
50
+ },
51
+ "homepage": "https://github.com/krymskyimaksym/react-api-client#readme",
52
+ "peerDependencies": {
53
+ "react": ">=16.8.0"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^20.11.0",
57
+ "@types/react": "^18.2.48",
58
+ "@typescript-eslint/eslint-plugin": "^6.19.0",
59
+ "@typescript-eslint/parser": "^6.19.0",
60
+ "eslint": "^8.56.0",
61
+ "eslint-config-prettier": "^9.1.0",
62
+ "eslint-plugin-prettier": "^5.1.3",
63
+ "eslint-plugin-react-hooks": "^4.6.0",
64
+ "prettier": "^3.2.4",
65
+ "tsup": "^8.0.1",
66
+ "typescript": "^5.3.3",
67
+ "vitest": "^1.2.0"
68
+ },
69
+ "engines": {
70
+ "node": ">=16.0.0"
71
+ }
72
+ }