axiomate 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.
Potentially problematic release.
This version of axiomate might be problematic. Click here for more details.
- package/README.md +148 -0
- package/package.json +22 -0
- package/src/_store.js +16 -0
- package/src/createApi.js +164 -0
- package/src/index.js +22 -0
- package/src/interceptors.js +62 -0
- package/src/useApi.js +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# axiomate
|
|
2
|
+
|
|
3
|
+
> Zero-boilerplate API client — define once, use everywhere.
|
|
4
|
+
|
|
5
|
+
Stop writing `axios.get(...)` with headers in every component. Define your entire backend API in one file, then call it anywhere with a single line.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install axiomate axios
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
### 1. Setup (sirf ek baar — `index.js` ya `App.js` mein)
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
import { setTokenGetter, addErrorInterceptor } from "axiomate";
|
|
23
|
+
|
|
24
|
+
// Token kahan se lena hai batao
|
|
25
|
+
setTokenGetter(() => localStorage.getItem("token"));
|
|
26
|
+
|
|
27
|
+
// Global error handling
|
|
28
|
+
addErrorInterceptor((error) => {
|
|
29
|
+
if (error.status === 401) {
|
|
30
|
+
window.location.href = "/login";
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. API define karo (ek file — `api.config.js`)
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
import { createApi } from "axiomate";
|
|
39
|
+
|
|
40
|
+
export const api = createApi({
|
|
41
|
+
baseUrl: "http://localhost:8080",
|
|
42
|
+
endpoints: {
|
|
43
|
+
login: { path: "/auth/login", method: "POST" },
|
|
44
|
+
getUser: { path: "/user/profile", method: "GET" },
|
|
45
|
+
updateUser: { path: "/user/update", method: "PUT" },
|
|
46
|
+
getProducts: { path: "/products", method: "GET" },
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 3. Anywhere use karo
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
import { api } from "./api.config";
|
|
55
|
+
|
|
56
|
+
// Simple call
|
|
57
|
+
const res = await api.login({ username: "ali", password: "1234" });
|
|
58
|
+
|
|
59
|
+
// GET with query params
|
|
60
|
+
const products = await api.getProducts({ page: 1, limit: 10 });
|
|
61
|
+
// → GET /products?page=1&limit=10
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 4. React Hook
|
|
65
|
+
|
|
66
|
+
```jsx
|
|
67
|
+
import { useApi } from "axiomate";
|
|
68
|
+
import { api } from "./api.config";
|
|
69
|
+
|
|
70
|
+
function ProfilePage() {
|
|
71
|
+
const { data, loading, error } = useApi(api.getUser);
|
|
72
|
+
|
|
73
|
+
if (loading) return <p>Loading...</p>;
|
|
74
|
+
if (error) return <p>{error.message}</p>;
|
|
75
|
+
return <h1>Hello, {data.name}</h1>;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## API Reference
|
|
82
|
+
|
|
83
|
+
### `createApi(config)`
|
|
84
|
+
|
|
85
|
+
| Option | Type | Required | Default | Description |
|
|
86
|
+
|--------|------|----------|---------|-------------|
|
|
87
|
+
| `baseUrl` | string | ✅ | — | Backend ka base URL |
|
|
88
|
+
| `endpoints` | object | ✅ | — | Endpoints ka map |
|
|
89
|
+
| `headers` | object | ❌ | `{}` | Extra default headers |
|
|
90
|
+
| `timeout` | number | ❌ | `10000` | Timeout in ms |
|
|
91
|
+
| `autoToken` | boolean | ❌ | `true` | Auto Bearer token attach |
|
|
92
|
+
|
|
93
|
+
### `setTokenGetter(fn)`
|
|
94
|
+
Token kahan se lena hai define karo.
|
|
95
|
+
```js
|
|
96
|
+
setTokenGetter(() => store.getState().auth.token);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `addRequestInterceptor(fn)`
|
|
100
|
+
Har request se pehle kuch karo.
|
|
101
|
+
```js
|
|
102
|
+
addRequestInterceptor((config) => {
|
|
103
|
+
config.headers["X-App-Version"] = "2.0";
|
|
104
|
+
return config;
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### `addResponseInterceptor(fn)`
|
|
109
|
+
Har response aane ke baad kuch karo.
|
|
110
|
+
```js
|
|
111
|
+
addResponseInterceptor((response) => {
|
|
112
|
+
console.log("Response:", response.config.url);
|
|
113
|
+
return response;
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `addErrorInterceptor(fn)`
|
|
118
|
+
Koi request fail ho toh handle karo.
|
|
119
|
+
```js
|
|
120
|
+
addErrorInterceptor((error) => {
|
|
121
|
+
if (error.status === 403) alert("Access denied!");
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `useApi(apiFn, options?)`
|
|
126
|
+
|
|
127
|
+
| Option | Type | Default | Description |
|
|
128
|
+
|--------|------|---------|-------------|
|
|
129
|
+
| `params` | object | `{}` | Default params |
|
|
130
|
+
| `immediate` | boolean | `true` | Mount pe call karo |
|
|
131
|
+
| `initialData` | any | `null` | Initial data value |
|
|
132
|
+
|
|
133
|
+
**Returns:** `{ data, loading, error, execute, reset }`
|
|
134
|
+
|
|
135
|
+
```jsx
|
|
136
|
+
// Auto call
|
|
137
|
+
const { data, loading, error } = useApi(api.getUser);
|
|
138
|
+
|
|
139
|
+
// Manual call
|
|
140
|
+
const { execute, loading } = useApi(api.login, { immediate: false });
|
|
141
|
+
await execute({ username, password });
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "axiomate",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Zero-boilerplate API client — define once, use everywhere.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"module": "src/index.js",
|
|
7
|
+
"keywords": ["api", "axios", "react", "http-client", "rest"],
|
|
8
|
+
"author": "Your Name",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"peerDependencies": {
|
|
11
|
+
"axios": "^1.0.0",
|
|
12
|
+
"react": ">=16.8.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"axios": "^1.6.0",
|
|
16
|
+
"react": "^18.0.0"
|
|
17
|
+
},
|
|
18
|
+
"files": ["src"],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "node tests/test.js"
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/_store.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* _store.js
|
|
3
|
+
* Internal shared state — library ke andar use hota hai
|
|
4
|
+
* Developer directly use na kare
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const _store = {
|
|
8
|
+
instance: null,
|
|
9
|
+
interceptors: {
|
|
10
|
+
request: [],
|
|
11
|
+
response: [],
|
|
12
|
+
error: [],
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
module.exports = { _store };
|
package/src/createApi.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
const axios = require("axios");
|
|
2
|
+
const { _store } = require("./_store");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Token getter — developer set karta hai apna logic
|
|
6
|
+
*/
|
|
7
|
+
let _getToken = () => localStorage.getItem("token");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* setTokenGetter — developer apna token logic provide kare
|
|
11
|
+
* @param {Function} fn - function jo token return kare
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* setTokenGetter(() => store.getState().auth.token);
|
|
15
|
+
*/
|
|
16
|
+
const setTokenGetter = (fn) => {
|
|
17
|
+
_getToken = fn;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* createApi — library ka core function
|
|
22
|
+
*
|
|
23
|
+
* @param {Object} config
|
|
24
|
+
* @param {string} config.baseUrl - Backend ka base URL
|
|
25
|
+
* @param {Object} config.endpoints - Sare endpoints ka map
|
|
26
|
+
* @param {Object} [config.headers] - Extra default headers
|
|
27
|
+
* @param {number} [config.timeout] - Request timeout ms (default: 10000)
|
|
28
|
+
* @param {boolean} [config.autoToken] - Auto Bearer token lagao (default: true)
|
|
29
|
+
*
|
|
30
|
+
* @returns {Object} - Har endpoint ke liye ek callable function
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const api = createApi({
|
|
34
|
+
* baseUrl: "http://localhost:8080",
|
|
35
|
+
* endpoints: {
|
|
36
|
+
* login: { path: "/auth/login", method: "POST" },
|
|
37
|
+
* getUser: { path: "/user/profile", method: "GET" },
|
|
38
|
+
* }
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* await api.login({ username: "ali", password: "1234" });
|
|
42
|
+
*/
|
|
43
|
+
const createApi = (config) => {
|
|
44
|
+
const {
|
|
45
|
+
baseUrl,
|
|
46
|
+
endpoints = {},
|
|
47
|
+
headers = {},
|
|
48
|
+
timeout = 10000,
|
|
49
|
+
autoToken = true,
|
|
50
|
+
} = config;
|
|
51
|
+
|
|
52
|
+
if (!baseUrl) throw new Error("[axiomate] baseUrl is required in createApi config.");
|
|
53
|
+
if (!endpoints || Object.keys(endpoints).length === 0) {
|
|
54
|
+
console.warn("[axiomate] No endpoints defined.");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Axios instance banao
|
|
58
|
+
_store.instance = axios.create({
|
|
59
|
+
baseURL: baseUrl,
|
|
60
|
+
timeout,
|
|
61
|
+
headers: {
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
...headers,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// ─── Request Interceptor ───────────────────────────────────────────────────
|
|
68
|
+
_store.instance.interceptors.request.use(
|
|
69
|
+
(axiosConfig) => {
|
|
70
|
+
// Auto token attach
|
|
71
|
+
if (autoToken) {
|
|
72
|
+
const token = _getToken();
|
|
73
|
+
if (token) {
|
|
74
|
+
axiosConfig.headers["Authorization"] = `Bearer ${token}`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Developer ke custom request interceptors chalao
|
|
79
|
+
let finalConfig = axiosConfig;
|
|
80
|
+
for (const fn of _store.interceptors.request) {
|
|
81
|
+
finalConfig = fn(finalConfig) || finalConfig;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return finalConfig;
|
|
85
|
+
},
|
|
86
|
+
(error) => Promise.reject(error)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// ─── Response Interceptor ─────────────────────────────────────────────────
|
|
90
|
+
_store.instance.interceptors.response.use(
|
|
91
|
+
(response) => {
|
|
92
|
+
let finalResponse = response;
|
|
93
|
+
for (const fn of _store.interceptors.response) {
|
|
94
|
+
finalResponse = fn(finalResponse) || finalResponse;
|
|
95
|
+
}
|
|
96
|
+
return finalResponse;
|
|
97
|
+
},
|
|
98
|
+
(error) => {
|
|
99
|
+
// Developer ke custom error interceptors chalao
|
|
100
|
+
for (const fn of _store.interceptors.error) {
|
|
101
|
+
fn(error);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Meaningful error message banao
|
|
105
|
+
const status = error?.response?.status;
|
|
106
|
+
const message =
|
|
107
|
+
error?.response?.data?.message ||
|
|
108
|
+
error?.response?.data?.error ||
|
|
109
|
+
error?.message ||
|
|
110
|
+
"Something went wrong";
|
|
111
|
+
|
|
112
|
+
const enhancedError = new Error(message);
|
|
113
|
+
enhancedError.status = status;
|
|
114
|
+
enhancedError.data = error?.response?.data;
|
|
115
|
+
enhancedError.original = error;
|
|
116
|
+
|
|
117
|
+
return Promise.reject(enhancedError);
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// ─── Endpoints ko functions mein convert karo ─────────────────────────────
|
|
122
|
+
const api = {};
|
|
123
|
+
|
|
124
|
+
for (const [name, endpoint] of Object.entries(endpoints)) {
|
|
125
|
+
const { path, method = "GET" } = endpoint;
|
|
126
|
+
|
|
127
|
+
if (!path) {
|
|
128
|
+
console.warn(`[axiomate] Endpoint "${name}" has no path defined. Skipping.`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Generated endpoint function
|
|
134
|
+
*
|
|
135
|
+
* @param {Object} [data] - POST/PUT ke liye request body
|
|
136
|
+
* @param {Object} [params] - GET ke liye query params
|
|
137
|
+
* @param {Object} [extraConfig] - Axios extra config override
|
|
138
|
+
*/
|
|
139
|
+
api[name] = (data = {}, params = {}, extraConfig = {}) => {
|
|
140
|
+
const upperMethod = method.toUpperCase();
|
|
141
|
+
|
|
142
|
+
const requestConfig = {
|
|
143
|
+
method: upperMethod,
|
|
144
|
+
url: path,
|
|
145
|
+
...extraConfig,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
if (upperMethod === "GET" || upperMethod === "DELETE") {
|
|
149
|
+
requestConfig.params = { ...params, ...data };
|
|
150
|
+
} else {
|
|
151
|
+
requestConfig.data = data;
|
|
152
|
+
if (Object.keys(params).length > 0) {
|
|
153
|
+
requestConfig.params = params;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return _store.instance(requestConfig).then((res) => res.data);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return api;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
module.exports = { createApi, setTokenGetter };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* axiomate
|
|
3
|
+
* ─────────────────────────────────────────────
|
|
4
|
+
* Zero-boilerplate API client.
|
|
5
|
+
* Define once, use everywhere.
|
|
6
|
+
*
|
|
7
|
+
* @author Your Name
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { createApi, setTokenGetter } = require("./createApi");
|
|
12
|
+
const { addRequestInterceptor, addResponseInterceptor, addErrorInterceptor } = require("./interceptors");
|
|
13
|
+
const { useApi } = require("./useApi");
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
createApi,
|
|
17
|
+
setTokenGetter,
|
|
18
|
+
addRequestInterceptor,
|
|
19
|
+
addResponseInterceptor,
|
|
20
|
+
addErrorInterceptor,
|
|
21
|
+
useApi,
|
|
22
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* interceptors.js
|
|
3
|
+
* Developer apne custom interceptors register kare
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { _store } = require("./_store");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* addRequestInterceptor
|
|
10
|
+
* Har request jaane se pehle ye function chalega
|
|
11
|
+
*
|
|
12
|
+
* @param {Function} fn - (axiosConfig) => axiosConfig
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* addRequestInterceptor((config) => {
|
|
16
|
+
* config.headers["X-App-Version"] = "1.0.0";
|
|
17
|
+
* return config;
|
|
18
|
+
* });
|
|
19
|
+
*/
|
|
20
|
+
const addRequestInterceptor = (fn) => {
|
|
21
|
+
_store.interceptors.request.push(fn);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* addResponseInterceptor
|
|
26
|
+
* Har successful response aane ke baad ye function chalega
|
|
27
|
+
*
|
|
28
|
+
* @param {Function} fn - (response) => response
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* addResponseInterceptor((response) => {
|
|
32
|
+
* console.log("Response received:", response.config.url);
|
|
33
|
+
* return response;
|
|
34
|
+
* });
|
|
35
|
+
*/
|
|
36
|
+
const addResponseInterceptor = (fn) => {
|
|
37
|
+
_store.interceptors.response.push(fn);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* addErrorInterceptor
|
|
42
|
+
* Koi bhi request fail ho toh ye function chalega
|
|
43
|
+
*
|
|
44
|
+
* @param {Function} fn - (error) => void
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* addErrorInterceptor((error) => {
|
|
48
|
+
* if (error.status === 401) {
|
|
49
|
+
* // logout user
|
|
50
|
+
* window.location.href = "/login";
|
|
51
|
+
* }
|
|
52
|
+
* });
|
|
53
|
+
*/
|
|
54
|
+
const addErrorInterceptor = (fn) => {
|
|
55
|
+
_store.interceptors.error.push(fn);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
addRequestInterceptor,
|
|
60
|
+
addResponseInterceptor,
|
|
61
|
+
addErrorInterceptor,
|
|
62
|
+
};
|
package/src/useApi.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const { useState, useEffect, useCallback, useRef } = require("react");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* useApi — React hook for API calls
|
|
5
|
+
* Loading, error, data sab automatic!
|
|
6
|
+
*
|
|
7
|
+
* @param {Function} apiFn - api.getUser jaise function
|
|
8
|
+
* @param {Object} [options]
|
|
9
|
+
* @param {any} [options.params] - Function ko pass karne wale params
|
|
10
|
+
* @param {boolean} [options.immediate] - Mount hote hi call karo (default: true)
|
|
11
|
+
* @param {any} [options.initialData] - Data ka initial value
|
|
12
|
+
*
|
|
13
|
+
* @returns {{ data, loading, error, execute, reset }}
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Auto call on mount:
|
|
17
|
+
* const { data, loading, error } = useApi(api.getUser);
|
|
18
|
+
*
|
|
19
|
+
* // Manual call (form submit etc):
|
|
20
|
+
* const { data, loading, execute } = useApi(api.login, { immediate: false });
|
|
21
|
+
* const handleSubmit = () => execute({ username, password });
|
|
22
|
+
*
|
|
23
|
+
* // With params:
|
|
24
|
+
* const { data } = useApi(api.getUserById, { params: { id: 5 } });
|
|
25
|
+
*/
|
|
26
|
+
const useApi = (apiFn, options = {}) => {
|
|
27
|
+
const {
|
|
28
|
+
params = {},
|
|
29
|
+
immediate = true,
|
|
30
|
+
initialData = null,
|
|
31
|
+
} = options;
|
|
32
|
+
|
|
33
|
+
const [data, setData] = useState(initialData);
|
|
34
|
+
const [loading, setLoading] = useState(immediate);
|
|
35
|
+
const [error, setError] = useState(null);
|
|
36
|
+
|
|
37
|
+
// Latest params ref — stale closure se bachne ke liye
|
|
38
|
+
const paramsRef = useRef(params);
|
|
39
|
+
paramsRef.current = params;
|
|
40
|
+
|
|
41
|
+
const execute = useCallback(
|
|
42
|
+
async (overrideParams = {}) => {
|
|
43
|
+
setLoading(true);
|
|
44
|
+
setError(null);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const mergedParams = { ...paramsRef.current, ...overrideParams };
|
|
48
|
+
const result = await apiFn(mergedParams);
|
|
49
|
+
setData(result);
|
|
50
|
+
return result;
|
|
51
|
+
} catch (err) {
|
|
52
|
+
setError(err);
|
|
53
|
+
throw err;
|
|
54
|
+
} finally {
|
|
55
|
+
setLoading(false);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
[apiFn]
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const reset = useCallback(() => {
|
|
62
|
+
setData(initialData);
|
|
63
|
+
setError(null);
|
|
64
|
+
setLoading(false);
|
|
65
|
+
}, [initialData]);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (immediate) {
|
|
69
|
+
execute();
|
|
70
|
+
}
|
|
71
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
72
|
+
|
|
73
|
+
return { data, loading, error, execute, reset };
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
module.exports = { useApi };
|