|
4 | 4 |
|
5 | 5 | # Antonio
|
6 | 6 |
|
7 |
| -A HTTP client that uses [axios](https://github.com/axios/axios) for making all HTTP requests and [@ackee/petrus](https://www.npmjs.com/package/@ackee/petrus) for adding an access token to the Authorization header. |
| 7 | +A HTTP client built on Fetch API with similar API to [axios](https://github.com/axios/axios). |
8 | 8 |
|
9 | 9 | ## Table of contents
|
10 | 10 |
|
11 | 11 | - [Installing](#installing)
|
12 |
| -- [Initialization](#initialization) |
| 12 | +- [Setup](#setup) |
13 | 13 | - [API](#api)
|
14 |
| - - [create](#api-create) |
15 |
| - - [Saga Effects](src/saga-effects/README.md) |
16 | 14 |
|
17 | 15 | ---
|
18 | 16 |
|
19 | 17 | ## <a name="installing"></a>Installing
|
20 | 18 |
|
21 |
| -Using yarn: |
22 |
| - |
23 |
| -```bash |
24 |
| -$ yarn add @ackee/antonio |
25 |
| -``` |
26 |
| - |
27 |
| -Using npm: |
28 |
| - |
29 | 19 | ```bash
|
30 |
| -$ npm i -S @ackee/antonio |
| 20 | +$ yarn add @ackee/antonio -S |
31 | 21 | ```
|
32 | 22 |
|
33 | 23 | ---
|
34 | 24 |
|
35 |
| -## <a name="initialization"></a>Initialization |
| 25 | +## <a name="setup"></a>Setup |
36 | 26 |
|
37 |
| -### 1. Create new instance |
| 27 | +Create a new instance and you're ready to go. |
38 | 28 |
|
39 | 29 | ```js
|
40 |
| -import * as Antonio from '@ackee/antonio'; |
41 |
| - |
42 |
| -const defaultRequestConfig = { |
43 |
| - baseURL: 'https://base-url.com/api', |
44 |
| -}; |
45 |
| - |
46 |
| -const { api, authApi, saga } = Antonio.create(defaultRequestConfig); |
| 30 | +import { create } from '@ackee/antonio'; |
47 | 31 |
|
48 |
| -export { api, authApi, saga }; |
| 32 | +export const api = create({ |
| 33 | + baseURL: 'https://jsonplaceholder.typicode.com/', |
| 34 | +}); |
49 | 35 | ```
|
50 | 36 |
|
51 |
| -### 2. Connect the saga |
52 |
| - |
53 |
| -Initializes the saga handlers generator. This should be passed along with your other sagas. |
| 37 | +## Usage Examples |
54 | 38 |
|
55 | 39 | ```js
|
56 |
| -import { saga as antonio } from 'Config/antonio'; |
57 |
| - |
58 |
| -export default function*() { |
59 |
| - // antonio's saga must come before @ackee/petrus saga |
60 |
| - yield all([antonio()]); |
61 |
| -} |
62 |
| -``` |
| 40 | +import { api } from '...'; |
63 | 41 |
|
64 |
| -## <a name="usage"></a>Usage |
| 42 | +api.get('/todos').then(({ data, request, response }) => { |
| 43 | + // ... |
| 44 | +}); |
65 | 45 |
|
66 |
| -### `api` - unauthorized requests |
67 |
| - |
68 |
| -See [available properties](#api-create-http-client) of the `api` object. |
69 |
| - |
70 |
| -```js |
71 |
| -import { api } from 'Config/antonio'; |
| 46 | +api.post('/todos', { |
| 47 | + title: 'Lorem ipsum', |
| 48 | +}); |
72 | 49 |
|
73 |
| -function* fetchTodo(todoId) { |
74 |
| - const response = yield api.get('/todos/:todoId', { |
75 |
| - // overwrite the default baseURL |
76 |
| - baseURL: 'https://jsonplaceholder.typicode.com/', |
| 50 | +api.put( |
| 51 | + '/todos/:id', |
| 52 | + { |
| 53 | + title: 'Not lorem ipsum', |
| 54 | + }, |
| 55 | + { |
77 | 56 | uriParams: {
|
78 |
| - todoId, |
| 57 | + id: '2', |
79 | 58 | },
|
80 |
| - }); |
81 |
| - |
82 |
| - return response.data; |
83 |
| -} |
84 |
| -``` |
85 |
| - |
86 |
| -### `authApi` - authorized requests |
87 |
| - |
88 |
| -By using methods under `authApi` object, it's guaranteed that each HTTP request is going to have an access token in its `Authorization` header. |
89 |
| - |
90 |
| -If the access token isn't available at the moment, the request is paused by `take(ACCESS_TOKEN_AVAILABLE)` effect, and timeout, if enabled, is set. See the [`accessTokenUnavailableTimeout`](#api-create-customConfig) for more details. |
91 |
| - |
92 |
| -See [available properties](#api-create-http-client) of the `authApi` object. |
93 |
| - |
94 |
| -```js |
95 |
| -import { authApi } from 'Config/antonio'; |
| 59 | + }, |
| 60 | +); |
96 | 61 |
|
97 |
| -function* fetchPost(postId) { |
98 |
| - const response = yield authApi.get(`/posts/${postId}`); |
| 62 | +api.get('/todos/:id', { |
| 63 | + uriParams: { |
| 64 | + id: '2', |
| 65 | + }, |
| 66 | +}); |
99 | 67 |
|
100 |
| - return response.data; |
101 |
| -} |
| 68 | +api.delete('/todos/:id', { |
| 69 | + uriParams: '2', |
| 70 | +}); |
102 | 71 | ```
|
103 | 72 |
|
104 |
| -> ### Shared `defaults` |
105 |
| -> |
106 |
| -> Even though `api` and `authApi` are created as separated axios instances, they share the same default request config object - [`api.defaults` and `authApi.defaults`](https://github.com/axios/axios#request-config). This issue/feature is caused by how axios is implemented and `@ackee/antonio` won't change it. Just don't be surprised, when you see the `Authorization` header also in requests created by the `api`. |
107 |
| -
|
108 | 73 | ---
|
109 | 74 |
|
110 | 75 | ## <a name="api"></a>API
|
111 | 76 |
|
112 |
| -### <a name="api-create"></a>`create(defaultRequestConfig: Object, customConfig: Object) => Object` |
113 |
| - |
114 |
| -This method receives two objects as arguments. |
115 |
| - |
116 |
| -- `defaultRequestConfig: Object` |
117 |
| - |
118 |
| - The `defaultRequestConfig` object is passed to axios as default request configuration. |
119 |
| - |
120 |
| - **Available properties**: |
| 77 | +### `create([config])` |
121 | 78 |
|
122 |
| - - [axios request config](https://github.com/axios/axios#request-config) |
123 |
| - - additional props: |
124 |
| - ```js |
125 |
| - // `uriParams` - Key-value object containing request uri params. Params that are found in url are replaced, rest is ignored. |
126 |
| - uriParams: { |
127 |
| - // ':todoId' will be replaced with '1' |
128 |
| - // '/todos/:todoId' -> '/todos/1' |
129 |
| - todoId: '1', |
130 |
| - }, |
131 |
| - ``` |
132 |
| - |
133 |
| -- <a name="api-create-customConfig"></a>`customConfig: Object` |
| 79 | +```js |
| 80 | +import * as Antonio from '@ackee/antonio'; |
134 | 81 |
|
135 |
| - The `customConfig` object offers following default options: |
| 82 | +const antonio = Antonio.create({ |
| 83 | + baseURL: 'https://some-domain.com/api/', |
| 84 | +}); |
| 85 | +``` |
136 | 86 |
|
137 |
| - ```js |
138 |
| - { |
139 |
| - // If `manageAuthHeader` is true, then when access token state changes, |
140 |
| - // the `setAuthHeader` is triggered. |
141 |
| - // If it's false, `setAuthHeader` won't be ever triggered. |
142 |
| - manageAuthHeader: true, |
143 |
| -
|
144 |
| - /** |
145 |
| - * If `manageAuthHeader` is enabled, `setAuthHeader` receives |
146 |
| - * object with default headers, when access token state changes. |
147 |
| - * @param {Object} headers - reference to axios default request headers object (https://github.com/axios/axios#custom-instance-defaults) |
148 |
| - * @param {Object|null} accessToken |
149 |
| - */ |
150 |
| - setAuthHeader(headers, accessToken) { |
151 |
| - if (accessToken) { |
152 |
| - // `common` indicates that it's a default header for all HTTP methods |
153 |
| - headers.common.Authorization = `Bearer ${accessToken.token}`; |
154 |
| - } else { |
155 |
| - delete headers.common.Authorization; |
156 |
| - } |
157 |
| - }, |
| 87 | +### Instance methods |
158 | 88 |
|
159 |
| - // If it's used `authApi` and access token isn't available, |
160 |
| - // there is optionable timeout with following default values: |
161 |
| - accessTokenUnavailableTimeout: { |
162 |
| - // enable / disable the timeout |
163 |
| - enabled: false, |
164 |
| - // set timeout duration for 30s |
165 |
| - duration: 1000 * 30, |
166 |
| - // if silent is true, then throw a custom error, |
167 |
| - // othewise API request will be made that fails, |
168 |
| - // and throws a server error |
169 |
| - silent: false, |
170 |
| - }, |
171 |
| - } |
172 |
| - ``` |
| 89 | +``` |
| 90 | +antonio#get(url[, config]) |
173 | 91 |
|
174 |
| -#### And returns: |
| 92 | +antonio#delete(url[, config]) |
175 | 93 |
|
176 |
| -- <a name="api-create-http-client"></a>`Object` |
| 94 | +antonio#head(url[, config]) |
177 | 95 |
|
178 |
| - #### `api`, `authApi` |
| 96 | +antonio#options(url[, config]) |
179 | 97 |
|
180 |
| - `api` and `authApi` have the same following properties: |
| 98 | +antonio#post(url[, data[, config]]) |
181 | 99 |
|
182 |
| - - `api.request(config)` |
183 |
| - - `api.get(url[, config])` |
184 |
| - - `api.delete(url[, config])` |
185 |
| - - `api.head(url[, config])` |
186 |
| - - `api.options(url[, config])` |
187 |
| - - `api.post(url[, data[, config]])` |
188 |
| - - `api.put(url[, data[, config]])` |
189 |
| - - `api.patch(url[, data[, config]])` |
190 |
| - - `api.getUri([config])` |
191 |
| - - [`api.defaults`](https://github.com/axios/axios#custom-instance-defaults) |
192 |
| - - [`api.interceptors`](https://github.com/axios/axios#interceptors) |
| 100 | +antonio#put(url[, data[, config]]) |
193 | 101 |
|
194 |
| - #### `saga` |
| 102 | +antonio#patch(url[, data[, config]]) |
| 103 | +``` |
195 | 104 |
|
196 |
| - Internal saga, primarily for communication with [`@ackee/petrus`](https://github.com/AckeeCZ/petrus). |
| 105 | +## Request config |
197 | 106 |
|
198 |
| -#### Example |
| 107 | +These are the available config options for making requests. None of these is required. |
199 | 108 |
|
200 | 109 | ```js
|
201 |
| -import * as Antonio from '@ackee/antonio'; |
202 |
| -
|
203 |
| -const { authApi } = Antonio.create( |
204 |
| - { |
205 |
| - baseURL: 'https://jsonplaceholder.typicode.com/', |
| 110 | +{ |
| 111 | + // `baseURL` will be prepended to `url` unless `url` is absolute. |
| 112 | + // It can be convenient to set `baseURL` for an instance of antonio to pass relative URLs |
| 113 | + // to methods of that instance. |
| 114 | + // Default: undefined |
| 115 | + baseUrl: 'https://jsonplaceholder.typicode.com/', |
| 116 | + |
| 117 | + // Default: "json" |
| 118 | + // Options: "json" | "blob" | "text" | "formData" | undefined |
| 119 | + responseType: "json", |
| 120 | + |
| 121 | + // Default: undefined |
| 122 | + // Options: object | undefined |
| 123 | + uriParams: { |
| 124 | + id: '2' |
206 | 125 | },
|
207 |
| - { |
208 |
| - // Customize setting of the authorization header |
209 |
| - // by providing a custom setAuthHeader method: |
210 |
| - setAuthHeader(headers, accessToken) { |
211 |
| - if (accessToken) { |
212 |
| - headers.common.Authorization = `${accessToken.tokenType} ${accessToken.token}`; |
213 |
| - } else { |
214 |
| - delete headers.common.Authorization; |
215 |
| - } |
216 |
| - }, |
217 |
| - }, |
218 |
| -); |
219 | 126 |
|
220 |
| -async function fetchTodo() { |
221 |
| - const response = await authApi.get('/todos/1'); |
222 |
| -
|
223 |
| - return response.data; |
| 127 | + // `headers` are custom headers to be sent |
| 128 | + // Must be a plain object or a Headers object |
| 129 | + // Default: undefined |
| 130 | + headers: new Headers({ |
| 131 | + 'X-Custom-Header': 1234 |
| 132 | + }), |
| 133 | + |
| 134 | + // `searchParams` are the URL parameters to be sent with the request |
| 135 | + // Must be a plain object or a URLSearchParams object |
| 136 | + // Default: undefined |
| 137 | + searchParams: new URLSearchParams({ |
| 138 | + query: 'foo' |
| 139 | + }), |
| 140 | + |
| 141 | + // Following props are pass to Request constructor, |
| 142 | + // see the official docs https://developer.mozilla.org/en-US/docs/Web/API/Request/Request |
| 143 | + mode: undefined, |
| 144 | + credentials: undefined, |
| 145 | + cache: undefined, |
| 146 | + redirect: undefined, |
| 147 | + referrer: undefined, |
| 148 | + referrerPolicy: undefined, |
| 149 | + integrity: undefined, |
| 150 | + keepalive: undefined, |
| 151 | + signal: undefined, |
224 | 152 | }
|
225 | 153 | ```
|
226 |
| - |
227 |
| -### <a name="api-saga-effects"></a> Saga Effects |
228 |
| - |
229 |
| -Custom Saga effects with built-in cancelation of API requests, [see the docs](src/saga-effects/README.md). |
0 commit comments