@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/LICENSE +21 -0
- package/README.md +303 -0
- package/dist/index.d.mts +152 -0
- package/dist/index.d.ts +152 -0
- package/dist/index.js +418 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +409 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Your Name
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# @krymskyimaksym/react-api-client
|
|
2
|
+
|
|
3
|
+
A lightweight, type-safe API client for React and React Native with built-in hooks for queries, mutations, and pagination.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎯 **Type-Safe**: Full TypeScript support with generic types
|
|
8
|
+
- 🪝 **React Hooks**: Built-in hooks for easy data fetching
|
|
9
|
+
- 📦 **Lightweight**: Zero dependencies (except React)
|
|
10
|
+
- 🔄 **Flexible**: Works with any HTTP client (fetch, axios, etc.)
|
|
11
|
+
- 🚀 **Modern**: ESM and CJS support
|
|
12
|
+
- 🎨 **Customizable**: Inject your own HTTP client and error handlers
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @krymskyimaksym/react-api-client
|
|
18
|
+
# or
|
|
19
|
+
yarn add @krymskyimaksym/react-api-client
|
|
20
|
+
# or
|
|
21
|
+
pnpm add @krymskyimaksym/react-api-client
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Configure the API Client
|
|
27
|
+
|
|
28
|
+
First, configure the global API client with your HTTP client and error handlers:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { configureApiClient } from '@krymskyimaksym/react-api-client';
|
|
32
|
+
import { router } from 'expo-router'; // or your router
|
|
33
|
+
|
|
34
|
+
// Create your HTTP client instance
|
|
35
|
+
const httpClient = {
|
|
36
|
+
async get(url, config) {
|
|
37
|
+
const response = await fetch(url);
|
|
38
|
+
return response.json();
|
|
39
|
+
},
|
|
40
|
+
async request(url, config) {
|
|
41
|
+
const response = await fetch(url, {
|
|
42
|
+
method: config.method,
|
|
43
|
+
body: JSON.stringify(config.data),
|
|
44
|
+
headers: { 'Content-Type': 'application/json' },
|
|
45
|
+
});
|
|
46
|
+
return response.json();
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Configure once at app startup
|
|
51
|
+
configureApiClient({
|
|
52
|
+
httpClient,
|
|
53
|
+
onUnauthorized: () => router.replace('/login'),
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 2. Define Your API Endpoints
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import apiClient, { apiMutation, apiPaginate } from '@krymskyimaksym/react-api-client';
|
|
61
|
+
|
|
62
|
+
// Types
|
|
63
|
+
type User = {
|
|
64
|
+
id: string;
|
|
65
|
+
name: string;
|
|
66
|
+
email: string;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
type CreateUserRequest = {
|
|
70
|
+
name: string;
|
|
71
|
+
email: string;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// GET endpoint
|
|
75
|
+
export const userApi = apiClient<User, { id: string }>('/api/users/:id');
|
|
76
|
+
|
|
77
|
+
// POST endpoint
|
|
78
|
+
export const createUserApi = apiMutation<User, CreateUserRequest>(
|
|
79
|
+
'/api/users',
|
|
80
|
+
{ method: 'POST' }
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Paginated endpoint
|
|
84
|
+
export const usersListApi = apiPaginate<
|
|
85
|
+
{ data: User[]; total: number },
|
|
86
|
+
User[]
|
|
87
|
+
>('/api/users');
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. Use in Components
|
|
91
|
+
|
|
92
|
+
#### Query (GET)
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
function UserProfile({ userId }: { userId: string }) {
|
|
96
|
+
const { data, isLoading, error, refetch } = userApi.useFetch(
|
|
97
|
+
{ id: userId },
|
|
98
|
+
{
|
|
99
|
+
onSuccess: (data) => console.log('User loaded:', data),
|
|
100
|
+
onError: (error) => console.error('Error:', error),
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (isLoading) return <LoadingSpinner />;
|
|
105
|
+
if (error) return <ErrorMessage error={error} />;
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div>
|
|
109
|
+
<h1>{data?.name}</h1>
|
|
110
|
+
<button onClick={refetch}>Refresh</button>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### Mutation (POST/PUT/PATCH/DELETE)
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
function CreateUserForm() {
|
|
120
|
+
const { mutate, isLoading, isSuccess, error } = createUserApi.useMutation({
|
|
121
|
+
onSuccess: (data) => {
|
|
122
|
+
console.log('User created:', data);
|
|
123
|
+
// Navigate or update UI
|
|
124
|
+
},
|
|
125
|
+
onError: (error) => {
|
|
126
|
+
console.error('Failed to create user:', error);
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const handleSubmit = (values: CreateUserRequest) => {
|
|
131
|
+
mutate(values);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<form onSubmit={handleSubmit}>
|
|
136
|
+
{/* form fields */}
|
|
137
|
+
<button type="submit" disabled={isLoading}>
|
|
138
|
+
{isLoading ? 'Creating...' : 'Create User'}
|
|
139
|
+
</button>
|
|
140
|
+
{isSuccess && <p>User created successfully!</p>}
|
|
141
|
+
{error && <p>Error: {error.message}</p>}
|
|
142
|
+
</form>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Pagination
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
function UsersList() {
|
|
151
|
+
const {
|
|
152
|
+
data,
|
|
153
|
+
isLoading,
|
|
154
|
+
hasNextPage,
|
|
155
|
+
fetchNextPage,
|
|
156
|
+
isFetchingNextPage,
|
|
157
|
+
} = usersListApi.usePaginate();
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<div>
|
|
161
|
+
{data.map(user => (
|
|
162
|
+
<UserCard key={user.id} user={user} />
|
|
163
|
+
))}
|
|
164
|
+
|
|
165
|
+
{hasNextPage && (
|
|
166
|
+
<button onClick={fetchNextPage} disabled={isFetchingNextPage}>
|
|
167
|
+
{isFetchingNextPage ? 'Loading...' : 'Load More'}
|
|
168
|
+
</button>
|
|
169
|
+
)}
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## API Reference
|
|
176
|
+
|
|
177
|
+
### `configureApiClient(config)`
|
|
178
|
+
|
|
179
|
+
Configure the global API client. Must be called before using any API functions.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
type ApiClientConfig = {
|
|
183
|
+
httpClient: IHttpClient;
|
|
184
|
+
onUnauthorized?: () => void | Promise<void>;
|
|
185
|
+
};
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `apiClient<ResponseType, RequestParamsType>(endpoint, config)`
|
|
189
|
+
|
|
190
|
+
Creates a query client for GET requests.
|
|
191
|
+
|
|
192
|
+
**Returns:**
|
|
193
|
+
- `fetch(params)` - Async function to fetch data
|
|
194
|
+
- `useFetch(params, options)` - React hook for data fetching
|
|
195
|
+
|
|
196
|
+
### `apiMutation<ResponseType, RequestParamsType>(endpoint, config)`
|
|
197
|
+
|
|
198
|
+
Creates a mutation client for POST/PUT/PATCH/DELETE requests.
|
|
199
|
+
|
|
200
|
+
**Returns:**
|
|
201
|
+
- `mutate(params)` - Async function to execute mutation
|
|
202
|
+
- `useMutation(options)` - React hook for mutations
|
|
203
|
+
|
|
204
|
+
### `apiPaginate<ResponseType, DataArrayType>(endpoint, config, options)`
|
|
205
|
+
|
|
206
|
+
Creates a paginated query client.
|
|
207
|
+
|
|
208
|
+
**Returns:**
|
|
209
|
+
- `usePaginate(params, options)` - React hook for paginated data
|
|
210
|
+
|
|
211
|
+
## Advanced Usage
|
|
212
|
+
|
|
213
|
+
### Dynamic Endpoints
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
const userApi = apiClient<User, { id: string }>(
|
|
217
|
+
(params) => `/api/users/${params.id}`
|
|
218
|
+
);
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Custom Data Extractors for Pagination
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const usersApi = apiPaginate<
|
|
225
|
+
{ users: User[]; count: number },
|
|
226
|
+
User[]
|
|
227
|
+
>(
|
|
228
|
+
'/api/users',
|
|
229
|
+
{ method: 'GET' },
|
|
230
|
+
{
|
|
231
|
+
dataExtractor: (response) => response.users,
|
|
232
|
+
totalExtractor: (response) => response.count,
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Imperative API Calls
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// Without hooks
|
|
241
|
+
const response = await userApi.fetch({ id: '123' });
|
|
242
|
+
if (response.status) {
|
|
243
|
+
console.log('Success:', response);
|
|
244
|
+
} else {
|
|
245
|
+
console.error('Error:', response.message);
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## HTTP Client Interface
|
|
250
|
+
|
|
251
|
+
Your HTTP client must implement this interface:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
interface IHttpClient {
|
|
255
|
+
get<T>(url: string, config?: RequestConfig): Promise<T>;
|
|
256
|
+
request<T>(url: string, config: RequestConfig): Promise<T>;
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Example with Axios
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
import axios from 'axios';
|
|
264
|
+
|
|
265
|
+
const httpClient = {
|
|
266
|
+
get: (url, config) => axios.get(url, config).then(res => res.data),
|
|
267
|
+
request: (url, config) => axios(url, config).then(res => res.data),
|
|
268
|
+
};
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Example with Fetch
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
const httpClient = {
|
|
275
|
+
async get(url, config) {
|
|
276
|
+
const params = new URLSearchParams(config?.params);
|
|
277
|
+
const response = await fetch(`${url}?${params}`);
|
|
278
|
+
if (!response.ok) throw new Error('Request failed');
|
|
279
|
+
return response.json();
|
|
280
|
+
},
|
|
281
|
+
async request(url, config) {
|
|
282
|
+
const response = await fetch(url, {
|
|
283
|
+
method: config.method,
|
|
284
|
+
headers: { 'Content-Type': 'application/json' },
|
|
285
|
+
body: config.data ? JSON.stringify(config.data) : undefined,
|
|
286
|
+
});
|
|
287
|
+
if (!response.ok) throw new Error('Request failed');
|
|
288
|
+
return response.json();
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## License
|
|
294
|
+
|
|
295
|
+
MIT
|
|
296
|
+
|
|
297
|
+
## Contributing
|
|
298
|
+
|
|
299
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
300
|
+
|
|
301
|
+
## Support
|
|
302
|
+
|
|
303
|
+
For issues and questions, please use [GitHub Issues](https://github.com/krymskyimaksym/react-api-client/issues).
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
type RequestConfig = {
|
|
2
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
3
|
+
requestParams?: Record<string, string>;
|
|
4
|
+
};
|
|
5
|
+
type ResponseWrapper<DataType, ErrorsType = unknown> = {
|
|
6
|
+
status: boolean;
|
|
7
|
+
message?: string;
|
|
8
|
+
errors?: ErrorsType;
|
|
9
|
+
} & DataType;
|
|
10
|
+
type UseFetchOptions<T> = {
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
refetchOnMount?: boolean;
|
|
13
|
+
onSuccess?: (data: T) => void;
|
|
14
|
+
onError?: (error: Error) => void;
|
|
15
|
+
};
|
|
16
|
+
type UseFetchResult<T> = {
|
|
17
|
+
data: T | null;
|
|
18
|
+
isLoading: boolean;
|
|
19
|
+
isRefetching: boolean;
|
|
20
|
+
error: Error | null;
|
|
21
|
+
refetch: () => Promise<void>;
|
|
22
|
+
};
|
|
23
|
+
type UsePaginateOptions<T> = {
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
initialPage?: number;
|
|
26
|
+
initialLimit?: number;
|
|
27
|
+
onSuccess?: (data: T) => void;
|
|
28
|
+
onError?: (error: Error) => void;
|
|
29
|
+
};
|
|
30
|
+
type UsePaginateResult<TData extends unknown[]> = {
|
|
31
|
+
data: TData;
|
|
32
|
+
currentPage: number;
|
|
33
|
+
totalPages: number | null;
|
|
34
|
+
total: number | null;
|
|
35
|
+
hasNextPage: boolean;
|
|
36
|
+
hasPreviousPage: boolean;
|
|
37
|
+
isLoading: boolean;
|
|
38
|
+
isFetchingNextPage: boolean;
|
|
39
|
+
error: Error | null;
|
|
40
|
+
fetchNextPage: () => Promise<void>;
|
|
41
|
+
fetchPreviousPage: () => Promise<void>;
|
|
42
|
+
refetch: () => Promise<void>;
|
|
43
|
+
reset: () => void;
|
|
44
|
+
};
|
|
45
|
+
type UseMutationOptions<TData, TVariables> = {
|
|
46
|
+
onMutate?: (variables: TVariables) => void | Promise<void>;
|
|
47
|
+
onSuccess?: (data: TData, variables: TVariables) => void | Promise<void>;
|
|
48
|
+
onError?: (error: Error, variables: TVariables) => void | Promise<void>;
|
|
49
|
+
onSettled?: (data: TData | null, error: Error | null, variables: TVariables) => void | Promise<void>;
|
|
50
|
+
};
|
|
51
|
+
type UseMutationResult<TData, TVariables> = {
|
|
52
|
+
data: TData | null;
|
|
53
|
+
error: Error | null;
|
|
54
|
+
isLoading: boolean;
|
|
55
|
+
isSuccess: boolean;
|
|
56
|
+
isError: boolean;
|
|
57
|
+
mutate: (variables: TVariables) => void;
|
|
58
|
+
mutateAsync: (variables: TVariables) => Promise<TData>;
|
|
59
|
+
reset: () => void;
|
|
60
|
+
};
|
|
61
|
+
interface IHttpClient {
|
|
62
|
+
get<T>(url: string, config?: {
|
|
63
|
+
params?: Record<string, unknown>;
|
|
64
|
+
}): Promise<T>;
|
|
65
|
+
request<T>(url: string, config: {
|
|
66
|
+
method?: string;
|
|
67
|
+
data?: Record<string, unknown>;
|
|
68
|
+
}): Promise<T>;
|
|
69
|
+
}
|
|
70
|
+
type ApiClientConfig = {
|
|
71
|
+
httpClient: IHttpClient;
|
|
72
|
+
onUnauthorized?: () => void | Promise<void>;
|
|
73
|
+
};
|
|
74
|
+
type ApiClientReturn<ResponseType, RequestParamsType, ErrorResponseType = unknown> = {
|
|
75
|
+
fetch: (params?: RequestParamsType) => Promise<ResponseWrapper<ResponseType, ErrorResponseType>>;
|
|
76
|
+
useFetch: (params?: RequestParamsType, options?: UseFetchOptions<ResponseWrapper<ResponseType, ErrorResponseType>>) => UseFetchResult<ResponseWrapper<ResponseType, ErrorResponseType>>;
|
|
77
|
+
};
|
|
78
|
+
type ApiMutationReturn<ResponseType, RequestParamsType, ErrorResponseType = unknown> = {
|
|
79
|
+
mutate: (params?: RequestParamsType) => Promise<ResponseWrapper<ResponseType, ErrorResponseType>>;
|
|
80
|
+
useMutation: (options?: UseMutationOptions<ResponseWrapper<ResponseType, ErrorResponseType>, RequestParamsType>) => UseMutationResult<ResponseWrapper<ResponseType, ErrorResponseType>, RequestParamsType>;
|
|
81
|
+
};
|
|
82
|
+
type ApiPaginateReturn<ResponseType, RequestParamsType, DataArrayType extends unknown[], ErrorResponseType = unknown> = {
|
|
83
|
+
usePaginate: (params?: Omit<RequestParamsType, 'page' | 'limit'>, options?: UsePaginateOptions<ResponseWrapper<ResponseType, ErrorResponseType>>) => UsePaginateResult<DataArrayType>;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Configure the API client globally
|
|
88
|
+
* @param config - API client configuration
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* import { configureApiClient } from '@krymskyimaksym/react-api-client';
|
|
92
|
+
* import { router } from 'expo-router';
|
|
93
|
+
*
|
|
94
|
+
* configureApiClient({
|
|
95
|
+
* httpClient: myHttpClient,
|
|
96
|
+
* onUnauthorized: () => router.replace('/login')
|
|
97
|
+
* });
|
|
98
|
+
*/
|
|
99
|
+
declare function configureApiClient(config: ApiClientConfig): void;
|
|
100
|
+
/**
|
|
101
|
+
* Get the current global configuration
|
|
102
|
+
* @throws Error if configuration is not set
|
|
103
|
+
*/
|
|
104
|
+
declare function getConfig(): ApiClientConfig;
|
|
105
|
+
/**
|
|
106
|
+
* Check if the API client is configured
|
|
107
|
+
*/
|
|
108
|
+
declare function isConfigured(): boolean;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Creates an API client for GET requests
|
|
112
|
+
* @param endpoint - URL string or function that generates URL from params
|
|
113
|
+
* @param fetchConfig - Request configuration
|
|
114
|
+
* @returns Object with fetch method and useFetch hook
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* const userApi = apiClient<User, { id: string }>('/api/users/:id')
|
|
118
|
+
* const { data } = userApi.useFetch({ id: '123' })
|
|
119
|
+
*/
|
|
120
|
+
declare function apiClient<ResponseType = void, RequestParamsType = void, ErrorResponseType = unknown>(endpoint: string | ((arg0: RequestParamsType) => string), fetchConfig?: RequestConfig): ApiClientReturn<ResponseType, RequestParamsType, ErrorResponseType>;
|
|
121
|
+
/**
|
|
122
|
+
* Creates an API client for mutation requests (POST/PUT/PATCH/DELETE)
|
|
123
|
+
* @param endpoint - URL string or function that generates URL from params
|
|
124
|
+
* @param fetchConfig - Request configuration
|
|
125
|
+
* @returns Object with mutate method and useMutation hook
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* const createUserApi = apiMutation<User, CreateUserRequest>('/api/users', { method: 'POST' })
|
|
129
|
+
* const { mutate, isLoading } = createUserApi.useMutation()
|
|
130
|
+
*/
|
|
131
|
+
declare function apiMutation<ResponseType = void, RequestParamsType = void, ErrorResponseType = unknown>(endpoint: string | ((arg0: RequestParamsType) => string), fetchConfig?: RequestConfig): ApiMutationReturn<ResponseType, RequestParamsType, ErrorResponseType>;
|
|
132
|
+
/**
|
|
133
|
+
* Creates an API client for paginated requests
|
|
134
|
+
* @param endpoint - URL string or function that generates URL from params
|
|
135
|
+
* @param fetchConfig - Request configuration
|
|
136
|
+
* @param options - Pagination options (data/total extractors)
|
|
137
|
+
* @returns Object with usePaginate hook
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* const usersApi = apiPaginate<UsersResponse, User[], { search?: string }>('/api/users')
|
|
141
|
+
* const { data, fetchNextPage, hasNextPage } = usersApi.usePaginate()
|
|
142
|
+
*/
|
|
143
|
+
declare function apiPaginate<ResponseType extends {
|
|
144
|
+
data: TData;
|
|
145
|
+
total?: number;
|
|
146
|
+
page?: number;
|
|
147
|
+
}, TData extends unknown[], RequestParamsType = void, ErrorResponseType = unknown>(endpoint: string | ((arg0: RequestParamsType) => string), fetchConfig?: RequestConfig, options?: {
|
|
148
|
+
dataExtractor?: (response: ResponseType) => TData;
|
|
149
|
+
totalExtractor?: (response: ResponseType) => number;
|
|
150
|
+
}): ApiPaginateReturn<ResponseType, RequestParamsType, TData, ErrorResponseType>;
|
|
151
|
+
|
|
152
|
+
export { type ApiClientConfig, type IHttpClient, type ResponseWrapper, type UseFetchOptions, type UseFetchResult, type UseMutationOptions, type UseMutationResult, type UsePaginateOptions, type UsePaginateResult, apiMutation, apiPaginate, configureApiClient, apiClient as default, getConfig, isConfigured };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
type RequestConfig = {
|
|
2
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
3
|
+
requestParams?: Record<string, string>;
|
|
4
|
+
};
|
|
5
|
+
type ResponseWrapper<DataType, ErrorsType = unknown> = {
|
|
6
|
+
status: boolean;
|
|
7
|
+
message?: string;
|
|
8
|
+
errors?: ErrorsType;
|
|
9
|
+
} & DataType;
|
|
10
|
+
type UseFetchOptions<T> = {
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
refetchOnMount?: boolean;
|
|
13
|
+
onSuccess?: (data: T) => void;
|
|
14
|
+
onError?: (error: Error) => void;
|
|
15
|
+
};
|
|
16
|
+
type UseFetchResult<T> = {
|
|
17
|
+
data: T | null;
|
|
18
|
+
isLoading: boolean;
|
|
19
|
+
isRefetching: boolean;
|
|
20
|
+
error: Error | null;
|
|
21
|
+
refetch: () => Promise<void>;
|
|
22
|
+
};
|
|
23
|
+
type UsePaginateOptions<T> = {
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
initialPage?: number;
|
|
26
|
+
initialLimit?: number;
|
|
27
|
+
onSuccess?: (data: T) => void;
|
|
28
|
+
onError?: (error: Error) => void;
|
|
29
|
+
};
|
|
30
|
+
type UsePaginateResult<TData extends unknown[]> = {
|
|
31
|
+
data: TData;
|
|
32
|
+
currentPage: number;
|
|
33
|
+
totalPages: number | null;
|
|
34
|
+
total: number | null;
|
|
35
|
+
hasNextPage: boolean;
|
|
36
|
+
hasPreviousPage: boolean;
|
|
37
|
+
isLoading: boolean;
|
|
38
|
+
isFetchingNextPage: boolean;
|
|
39
|
+
error: Error | null;
|
|
40
|
+
fetchNextPage: () => Promise<void>;
|
|
41
|
+
fetchPreviousPage: () => Promise<void>;
|
|
42
|
+
refetch: () => Promise<void>;
|
|
43
|
+
reset: () => void;
|
|
44
|
+
};
|
|
45
|
+
type UseMutationOptions<TData, TVariables> = {
|
|
46
|
+
onMutate?: (variables: TVariables) => void | Promise<void>;
|
|
47
|
+
onSuccess?: (data: TData, variables: TVariables) => void | Promise<void>;
|
|
48
|
+
onError?: (error: Error, variables: TVariables) => void | Promise<void>;
|
|
49
|
+
onSettled?: (data: TData | null, error: Error | null, variables: TVariables) => void | Promise<void>;
|
|
50
|
+
};
|
|
51
|
+
type UseMutationResult<TData, TVariables> = {
|
|
52
|
+
data: TData | null;
|
|
53
|
+
error: Error | null;
|
|
54
|
+
isLoading: boolean;
|
|
55
|
+
isSuccess: boolean;
|
|
56
|
+
isError: boolean;
|
|
57
|
+
mutate: (variables: TVariables) => void;
|
|
58
|
+
mutateAsync: (variables: TVariables) => Promise<TData>;
|
|
59
|
+
reset: () => void;
|
|
60
|
+
};
|
|
61
|
+
interface IHttpClient {
|
|
62
|
+
get<T>(url: string, config?: {
|
|
63
|
+
params?: Record<string, unknown>;
|
|
64
|
+
}): Promise<T>;
|
|
65
|
+
request<T>(url: string, config: {
|
|
66
|
+
method?: string;
|
|
67
|
+
data?: Record<string, unknown>;
|
|
68
|
+
}): Promise<T>;
|
|
69
|
+
}
|
|
70
|
+
type ApiClientConfig = {
|
|
71
|
+
httpClient: IHttpClient;
|
|
72
|
+
onUnauthorized?: () => void | Promise<void>;
|
|
73
|
+
};
|
|
74
|
+
type ApiClientReturn<ResponseType, RequestParamsType, ErrorResponseType = unknown> = {
|
|
75
|
+
fetch: (params?: RequestParamsType) => Promise<ResponseWrapper<ResponseType, ErrorResponseType>>;
|
|
76
|
+
useFetch: (params?: RequestParamsType, options?: UseFetchOptions<ResponseWrapper<ResponseType, ErrorResponseType>>) => UseFetchResult<ResponseWrapper<ResponseType, ErrorResponseType>>;
|
|
77
|
+
};
|
|
78
|
+
type ApiMutationReturn<ResponseType, RequestParamsType, ErrorResponseType = unknown> = {
|
|
79
|
+
mutate: (params?: RequestParamsType) => Promise<ResponseWrapper<ResponseType, ErrorResponseType>>;
|
|
80
|
+
useMutation: (options?: UseMutationOptions<ResponseWrapper<ResponseType, ErrorResponseType>, RequestParamsType>) => UseMutationResult<ResponseWrapper<ResponseType, ErrorResponseType>, RequestParamsType>;
|
|
81
|
+
};
|
|
82
|
+
type ApiPaginateReturn<ResponseType, RequestParamsType, DataArrayType extends unknown[], ErrorResponseType = unknown> = {
|
|
83
|
+
usePaginate: (params?: Omit<RequestParamsType, 'page' | 'limit'>, options?: UsePaginateOptions<ResponseWrapper<ResponseType, ErrorResponseType>>) => UsePaginateResult<DataArrayType>;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Configure the API client globally
|
|
88
|
+
* @param config - API client configuration
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* import { configureApiClient } from '@krymskyimaksym/react-api-client';
|
|
92
|
+
* import { router } from 'expo-router';
|
|
93
|
+
*
|
|
94
|
+
* configureApiClient({
|
|
95
|
+
* httpClient: myHttpClient,
|
|
96
|
+
* onUnauthorized: () => router.replace('/login')
|
|
97
|
+
* });
|
|
98
|
+
*/
|
|
99
|
+
declare function configureApiClient(config: ApiClientConfig): void;
|
|
100
|
+
/**
|
|
101
|
+
* Get the current global configuration
|
|
102
|
+
* @throws Error if configuration is not set
|
|
103
|
+
*/
|
|
104
|
+
declare function getConfig(): ApiClientConfig;
|
|
105
|
+
/**
|
|
106
|
+
* Check if the API client is configured
|
|
107
|
+
*/
|
|
108
|
+
declare function isConfigured(): boolean;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Creates an API client for GET requests
|
|
112
|
+
* @param endpoint - URL string or function that generates URL from params
|
|
113
|
+
* @param fetchConfig - Request configuration
|
|
114
|
+
* @returns Object with fetch method and useFetch hook
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* const userApi = apiClient<User, { id: string }>('/api/users/:id')
|
|
118
|
+
* const { data } = userApi.useFetch({ id: '123' })
|
|
119
|
+
*/
|
|
120
|
+
declare function apiClient<ResponseType = void, RequestParamsType = void, ErrorResponseType = unknown>(endpoint: string | ((arg0: RequestParamsType) => string), fetchConfig?: RequestConfig): ApiClientReturn<ResponseType, RequestParamsType, ErrorResponseType>;
|
|
121
|
+
/**
|
|
122
|
+
* Creates an API client for mutation requests (POST/PUT/PATCH/DELETE)
|
|
123
|
+
* @param endpoint - URL string or function that generates URL from params
|
|
124
|
+
* @param fetchConfig - Request configuration
|
|
125
|
+
* @returns Object with mutate method and useMutation hook
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* const createUserApi = apiMutation<User, CreateUserRequest>('/api/users', { method: 'POST' })
|
|
129
|
+
* const { mutate, isLoading } = createUserApi.useMutation()
|
|
130
|
+
*/
|
|
131
|
+
declare function apiMutation<ResponseType = void, RequestParamsType = void, ErrorResponseType = unknown>(endpoint: string | ((arg0: RequestParamsType) => string), fetchConfig?: RequestConfig): ApiMutationReturn<ResponseType, RequestParamsType, ErrorResponseType>;
|
|
132
|
+
/**
|
|
133
|
+
* Creates an API client for paginated requests
|
|
134
|
+
* @param endpoint - URL string or function that generates URL from params
|
|
135
|
+
* @param fetchConfig - Request configuration
|
|
136
|
+
* @param options - Pagination options (data/total extractors)
|
|
137
|
+
* @returns Object with usePaginate hook
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* const usersApi = apiPaginate<UsersResponse, User[], { search?: string }>('/api/users')
|
|
141
|
+
* const { data, fetchNextPage, hasNextPage } = usersApi.usePaginate()
|
|
142
|
+
*/
|
|
143
|
+
declare function apiPaginate<ResponseType extends {
|
|
144
|
+
data: TData;
|
|
145
|
+
total?: number;
|
|
146
|
+
page?: number;
|
|
147
|
+
}, TData extends unknown[], RequestParamsType = void, ErrorResponseType = unknown>(endpoint: string | ((arg0: RequestParamsType) => string), fetchConfig?: RequestConfig, options?: {
|
|
148
|
+
dataExtractor?: (response: ResponseType) => TData;
|
|
149
|
+
totalExtractor?: (response: ResponseType) => number;
|
|
150
|
+
}): ApiPaginateReturn<ResponseType, RequestParamsType, TData, ErrorResponseType>;
|
|
151
|
+
|
|
152
|
+
export { type ApiClientConfig, type IHttpClient, type ResponseWrapper, type UseFetchOptions, type UseFetchResult, type UseMutationOptions, type UseMutationResult, type UsePaginateOptions, type UsePaginateResult, apiMutation, apiPaginate, configureApiClient, apiClient as default, getConfig, isConfigured };
|