@brickflow/http 0.0.14 → 0.0.16
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/README.md +272 -288
- package/dist/create-get.d.ts +12 -0
- package/dist/create-get.d.ts.map +1 -0
- package/dist/http.d.ts +46 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +171 -0
- package/dist/index.mjs.map +1 -0
- package/dist/nuxt-DAmJOX58.js +233 -0
- package/dist/nuxt-DAmJOX58.js.map +1 -0
- package/dist/nuxt.d.ts +55 -0
- package/dist/nuxt.d.ts.map +1 -0
- package/dist/nuxt.mjs +2 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +30 -18
- package/src/app.d.ts +11 -0
- package/src/create-get.ts +43 -0
- package/src/http.ts +286 -0
- package/src/index.ts +3 -0
- package/src/nuxt.ts +355 -0
- package/src/utils.ts +50 -0
- package/dist/module.d.mts +0 -73
- package/dist/module.json +0 -12
- package/dist/module.mjs +0 -59
- package/dist/runtime/composables/useHttp.d.ts +0 -27
- package/dist/runtime/composables/useHttp.js +0 -197
- package/dist/runtime/http/client.d.ts +0 -3
- package/dist/runtime/http/client.js +0 -217
- package/dist/runtime/plugin.d.ts +0 -7
- package/dist/runtime/plugin.js +0 -56
- package/dist/runtime/types.d.ts +0 -21
- package/dist/runtime/utils/helpers.d.ts +0 -5
- package/dist/runtime/utils/helpers.js +0 -14
- package/dist/runtime/utils/index.d.ts +0 -6
- package/dist/runtime/utils/index.js +0 -5
- package/dist/runtime/utils/indexeddb.d.ts +0 -14
- package/dist/runtime/utils/indexeddb.js +0 -222
- package/dist/runtime/utils/middleware.d.ts +0 -8
- package/dist/runtime/utils/middleware.js +0 -20
- package/dist/runtime/utils/shared.d.ts +0 -83
- package/dist/runtime/utils/shared.js +0 -50
- package/dist/runtime/utils/typed.d.ts +0 -46
- package/dist/runtime/utils/typed.js +0 -9
- package/dist/types.d.mts +0 -11
package/README.md
CHANGED
|
@@ -1,406 +1,390 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @brickflow/http
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Минимальный HTTP-пакет для brickflow:
|
|
4
4
|
|
|
5
|
-
- `
|
|
6
|
-
- `
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
- optional client cache via IndexedDB
|
|
10
|
-
- typed URL literals for `get`, `post`, and `useHttp`
|
|
5
|
+
- `createHttp` для низкоуровневых `GET`/`POST`
|
|
6
|
+
- `createUseHttp` для Nuxt state-обёртки с cache, SSR и ручным `fetch`
|
|
7
|
+
- `defineGet` для типизированных endpoint-фабрик
|
|
8
|
+
- `createUseCase` для DI-паттерна в доменных модулях
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
Актуальные примеры в репозитории лежат в `apps/playground`.
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
pnpm add @brickflow/http
|
|
16
|
-
```
|
|
12
|
+
## Exports
|
|
17
13
|
|
|
18
|
-
|
|
14
|
+
Рекомендуемый импорт:
|
|
19
15
|
|
|
20
16
|
```ts
|
|
21
|
-
|
|
22
|
-
modules: ['@brickflow/http'],
|
|
23
|
-
})
|
|
17
|
+
import { createHttp, createUseHttp, createUseCase, defineGet } from '@brickflow/http'
|
|
24
18
|
```
|
|
25
19
|
|
|
26
|
-
|
|
20
|
+
Из пакета также экспортируются:
|
|
21
|
+
|
|
22
|
+
- `createURL`
|
|
23
|
+
- `hashData`
|
|
24
|
+
- типы `HttpClient`, `HttpConfig`, `HttpEndpoint`, `HttpKey`, `HttpParam`, `HttpResponse`, `HttpResponseData`
|
|
25
|
+
- Nuxt-типы `UseHttpFn`, `UseHttpOptions`, `UseHttpResult`
|
|
26
|
+
|
|
27
|
+
Сабпуть `@brickflow/http/nuxt` остаётся доступным, но в текущей кодовой базе `playground` использует импорты из корня.
|
|
28
|
+
|
|
29
|
+
## Типизация API
|
|
30
|
+
|
|
31
|
+
Пакет не знает схему вашего API заранее. Её нужно объявить в проекте через `declare module '@brickflow/http'`.
|
|
32
|
+
|
|
33
|
+
Актуальный пример из `apps/playground/types/http.d.ts`:
|
|
27
34
|
|
|
28
35
|
```ts
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
36
|
+
import type { HttpKey, HttpResponseData } from '@brickflow/http'
|
|
37
|
+
|
|
38
|
+
type Convention = {
|
|
39
|
+
'/api/http-error-demo': null
|
|
40
|
+
'/products':
|
|
41
|
+
| {
|
|
42
|
+
limit: number
|
|
43
|
+
products: {
|
|
44
|
+
category: string
|
|
45
|
+
id: number
|
|
46
|
+
price: number
|
|
47
|
+
rating: number
|
|
48
|
+
thumbnail: string
|
|
49
|
+
title: string
|
|
50
|
+
}[]
|
|
51
|
+
skip: number
|
|
52
|
+
total: number
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
message: 'error'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare module '@brickflow/http' {
|
|
60
|
+
interface HttpConfig<TKey extends HttpKey = HttpKey> {
|
|
61
|
+
ignore?: (data: HttpResponseData<TKey>) => boolean
|
|
62
|
+
}
|
|
50
63
|
|
|
51
|
-
|
|
64
|
+
interface HttpEndpoint extends Convention {}
|
|
65
|
+
}
|
|
52
66
|
|
|
53
|
-
|
|
67
|
+
export {}
|
|
68
|
+
```
|
|
54
69
|
|
|
55
|
-
|
|
56
|
-
| ------------------- | ------------------ |
|
|
57
|
-
| `baseURL` | `''` |
|
|
58
|
-
| `cache` | `true` |
|
|
59
|
-
| `cacheDbName` | `'smart-cache-v2'` |
|
|
60
|
-
| `cacheStoreName` | `'data'` |
|
|
61
|
-
| `cacheTtlMs` | `604800000` |
|
|
62
|
-
| `clientEnvHeader` | `true` |
|
|
63
|
-
| `defaultHeaders` | `{}` |
|
|
64
|
-
| `disableCacheInDev` | `true` |
|
|
65
|
-
| `requestTimeoutMs` | `80000` |
|
|
66
|
-
| `retry.delay` | `300` |
|
|
67
|
-
| `retry.retries` | `3` |
|
|
70
|
+
Что это даёт:
|
|
68
71
|
|
|
69
|
-
|
|
72
|
+
- `HttpKey` становится union из endpoint-ключей
|
|
73
|
+
- `httpClient.get('/products')` получает корректный тип ответа
|
|
74
|
+
- `createGet<Params>()('/products')` наследует тот же контракт
|
|
75
|
+
- `HttpConfig` можно расширять своими полями, как в playground через `ignore`
|
|
70
76
|
|
|
71
|
-
|
|
72
|
-
- in dev, `Client-Env: development` is added when `clientEnvHeader` is enabled
|
|
73
|
-
- `5xx` and `451` responses with `{ status: 'error' }` trigger Nuxt `showError()`
|
|
77
|
+
Если нужен типизированный `error` в `UseHttpResult`, дополнительно расширьте `HttpErrorMap`. По умолчанию `error` имеет тип `never`, а разделение между `data` и `error` зависит только от runtime `isError`.
|
|
74
78
|
|
|
75
|
-
##
|
|
79
|
+
## createHttp
|
|
76
80
|
|
|
77
|
-
|
|
81
|
+
Актуальный пример из `apps/playground/plugins/01.di.ts`:
|
|
78
82
|
|
|
79
83
|
```ts
|
|
80
|
-
|
|
84
|
+
import { createHttp } from '@brickflow/http'
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
import { handlePlaygroundResponse } from '~/core/http-error'
|
|
87
|
+
|
|
88
|
+
const httpClient = createHttp({
|
|
89
|
+
baseURL: 'https://dummyjson.com',
|
|
90
|
+
headers: {
|
|
91
|
+
'X-Playground-Http': 'playground-runtime',
|
|
85
92
|
},
|
|
93
|
+
onResponse: handlePlaygroundResponse,
|
|
86
94
|
})
|
|
87
|
-
|
|
88
|
-
if ('status' in response.data && response.data.status === 'error') {
|
|
89
|
-
console.error(response.data.message)
|
|
90
|
-
} else {
|
|
91
|
-
console.log(response.data.name)
|
|
92
|
-
}
|
|
93
95
|
```
|
|
94
96
|
|
|
95
|
-
|
|
97
|
+
Поддерживаемые опции:
|
|
96
98
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
- `baseURL: string`
|
|
100
|
+
- `arrayMode?: 'json' | 'repeat'`
|
|
101
|
+
- `fetch?: typeof fetch`
|
|
102
|
+
- `headers?: Record<string, string | undefined> | (() => Record<string, string | undefined>)`
|
|
103
|
+
- `onResponse?: (response) => void | Promise<void>`
|
|
104
|
+
- `timeout?: number`
|
|
105
|
+
|
|
106
|
+
### GET
|
|
99
107
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
{
|
|
103
|
-
|
|
108
|
+
```ts
|
|
109
|
+
const response = await httpClient.get('/products', {
|
|
110
|
+
params: {
|
|
111
|
+
limit: 10,
|
|
104
112
|
},
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
},
|
|
113
|
+
retry: {
|
|
114
|
+
delay: 300,
|
|
115
|
+
retries: 3,
|
|
109
116
|
},
|
|
110
|
-
)
|
|
117
|
+
})
|
|
111
118
|
```
|
|
112
119
|
|
|
113
|
-
|
|
120
|
+
Особенности:
|
|
114
121
|
|
|
115
|
-
|
|
122
|
+
- query строится из `params`
|
|
123
|
+
- `GET` поддерживает retry для `429` и `5xx`
|
|
124
|
+
- delay экспоненциальный: `delay * 2 ** attempt`
|
|
125
|
+
- `signal` можно передать как один `AbortSignal` или массив `AbortSignal[]`
|
|
116
126
|
|
|
117
|
-
|
|
118
|
-
- `error`
|
|
119
|
-
- `pending`
|
|
120
|
-
- `pendingCache`
|
|
121
|
-
- `hasFirstData`
|
|
122
|
-
- `hasFreshData`
|
|
123
|
-
- `fetch()`
|
|
124
|
-
|
|
125
|
-
Basic example:
|
|
127
|
+
### POST
|
|
126
128
|
|
|
127
129
|
```ts
|
|
128
|
-
const
|
|
129
|
-
server: true,
|
|
130
|
-
url: '/users',
|
|
131
|
-
})
|
|
130
|
+
const { data } = await httpClient.post('/api/http-error-demo')
|
|
132
131
|
```
|
|
133
132
|
|
|
134
|
-
|
|
133
|
+
Для `POST`:
|
|
135
134
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
},
|
|
141
|
-
url: '/users',
|
|
142
|
-
})
|
|
135
|
+
- `Record<string, unknown>` сериализуется в JSON
|
|
136
|
+
- `FormData` отправляется как есть
|
|
137
|
+
- retry нет
|
|
138
|
+
- если response не JSON, пакет вернёт пустой объект в `data`
|
|
143
139
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
```
|
|
140
|
+
### Расширение `HttpConfig`
|
|
141
|
+
|
|
142
|
+
Так как `HttpConfig` расширяемый, можно прокидывать свои поля в запрос и читать их в `onResponse`.
|
|
148
143
|
|
|
149
|
-
|
|
144
|
+
Пример из playground:
|
|
150
145
|
|
|
151
146
|
```ts
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (!cached) {
|
|
155
|
-
console.log('fresh profile payload', payload)
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
url: '/profile',
|
|
147
|
+
httpClient.get('/products', {
|
|
148
|
+
ignore: (data) => data?.limit === 20,
|
|
159
149
|
})
|
|
160
150
|
```
|
|
161
151
|
|
|
162
|
-
## Typed Routes
|
|
163
|
-
|
|
164
|
-
If you want `'/users'` and other URL literals to infer `params`, `data`, `error`, and `body` automatically, extend the global `BrickflowHttpRouteMap`.
|
|
165
|
-
|
|
166
|
-
Create a declaration file, for example `types/brickflow-http.d.ts`:
|
|
167
|
-
|
|
168
152
|
```ts
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
'/api/user': {
|
|
174
|
-
data: { id: string; name: string }
|
|
175
|
-
error: HttpErrorPayload & { code?: 'NOT_FOUND' }
|
|
176
|
-
params: { id: string }
|
|
177
|
-
}
|
|
178
|
-
'/api/posts': {
|
|
179
|
-
data: Array<{ id: string; title: string }>
|
|
180
|
-
params: { page?: number }
|
|
181
|
-
}
|
|
182
|
-
'/api/posts/create': {
|
|
183
|
-
body: { title: string }
|
|
184
|
-
data: { id: string }
|
|
185
|
-
}
|
|
186
|
-
}
|
|
153
|
+
export function handlePlaygroundResponse(
|
|
154
|
+
response: Parameters<NonNullable<CreateHttpOptions['onResponse']>>[0],
|
|
155
|
+
): void {
|
|
156
|
+
console.log(response.config.ignore?.(response.data))
|
|
187
157
|
}
|
|
188
|
-
|
|
189
|
-
export {}
|
|
190
158
|
```
|
|
191
159
|
|
|
192
|
-
|
|
160
|
+
## createUseHttp
|
|
193
161
|
|
|
194
|
-
|
|
162
|
+
В `apps/playground/core/http.ts` app-level инстанс создаётся один раз:
|
|
195
163
|
|
|
196
164
|
```ts
|
|
197
|
-
|
|
165
|
+
import { createUseHttp, defineGet } from '@brickflow/http'
|
|
198
166
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
167
|
+
import { dbDeleteKeysWithPart, dbGet, dbSafeSet } from './indexdb'
|
|
168
|
+
|
|
169
|
+
const CACHE_DB_NAME = 'smart-cache-v2'
|
|
170
|
+
const CACHE_STORE_NAME = 'playground-http'
|
|
171
|
+
|
|
172
|
+
const useHttp = createUseHttp({
|
|
173
|
+
getCache: () => ({
|
|
174
|
+
deleteKeysWithPart: async (part: string) => await dbDeleteKeysWithPart(part, CACHE_DB_NAME, CACHE_STORE_NAME),
|
|
175
|
+
get: async <T>(key: string) => await dbGet<T>(key, CACHE_DB_NAME, CACHE_STORE_NAME),
|
|
176
|
+
set: async <T>(key: string, value: T, ttl: number) =>
|
|
177
|
+
await dbSafeSet<T>(key, value, CACHE_DB_NAME, CACHE_STORE_NAME, ttl),
|
|
178
|
+
}),
|
|
179
|
+
getHttpClient: () => useNuxtApp().$http,
|
|
180
|
+
isDev: () => false,
|
|
203
181
|
})
|
|
182
|
+
|
|
183
|
+
export const createGet = defineGet(useHttp)
|
|
204
184
|
```
|
|
205
185
|
|
|
206
|
-
|
|
186
|
+
Зависимости:
|
|
207
187
|
|
|
208
|
-
|
|
209
|
-
|
|
188
|
+
- `getHttpClient` обязательно
|
|
189
|
+
- `getCache` опционально
|
|
190
|
+
- `isDev` опционально
|
|
191
|
+
- `isError` опционально
|
|
192
|
+
- `ttl` опционально
|
|
193
|
+
- `channelName` опционально
|
|
210
194
|
|
|
211
|
-
|
|
212
|
-
title: 'New post',
|
|
213
|
-
})
|
|
214
|
-
```
|
|
195
|
+
`createUseHttp` возвращает функцию `useHttp(options)`, которая создаёт реактивное состояние запроса:
|
|
215
196
|
|
|
216
|
-
|
|
197
|
+
- `data`
|
|
198
|
+
- `error`
|
|
199
|
+
- `pending`
|
|
200
|
+
- `pendingCache`
|
|
201
|
+
- `hasFirstData`
|
|
202
|
+
- `hasFreshData`
|
|
203
|
+
- `fetch(params?, { signal? })`
|
|
217
204
|
|
|
218
|
-
|
|
219
|
-
const posts = await useHttp({
|
|
220
|
-
initParams: {
|
|
221
|
-
page: 1,
|
|
222
|
-
},
|
|
223
|
-
url: '/api/posts',
|
|
224
|
-
})
|
|
205
|
+
### Опции useHttp
|
|
225
206
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
207
|
+
- `url`
|
|
208
|
+
- `initParams`
|
|
209
|
+
- `mapParams`
|
|
210
|
+
- `effect`
|
|
211
|
+
- `isError`
|
|
212
|
+
- `lazy`
|
|
213
|
+
- `server`
|
|
230
214
|
|
|
231
|
-
|
|
215
|
+
`effect(data, config)` вызывается и для cache, и для fresh-response:
|
|
232
216
|
|
|
233
|
-
|
|
217
|
+
- `config.cached === true` для cache
|
|
218
|
+
- `config.cached === false` для сети
|
|
219
|
+
- `config.params` содержит уже применённые params
|
|
234
220
|
|
|
235
|
-
|
|
221
|
+
## defineGet
|
|
222
|
+
|
|
223
|
+
`defineGet` связывает app-level `useHttp` с endpoint-фабриками:
|
|
236
224
|
|
|
237
225
|
```ts
|
|
238
|
-
|
|
226
|
+
const useHttp = createUseHttp({ ... })
|
|
239
227
|
|
|
240
|
-
const
|
|
241
|
-
|
|
228
|
+
export const createGet = defineGet(useHttp)
|
|
229
|
+
```
|
|
242
230
|
|
|
243
|
-
|
|
244
|
-
params: { id: '42' },
|
|
245
|
-
})
|
|
231
|
+
Дальше в домене можно описывать запросы коротко и типизированно:
|
|
246
232
|
|
|
247
|
-
|
|
248
|
-
|
|
233
|
+
```ts
|
|
234
|
+
products: createGet<{
|
|
235
|
+
limit: number
|
|
236
|
+
}>()('/products')
|
|
249
237
|
```
|
|
250
238
|
|
|
251
|
-
|
|
239
|
+
`defineGet` поддерживает два слоя настроек:
|
|
252
240
|
|
|
253
|
-
|
|
254
|
-
import { createHttpClient, createTypedHttpClient } from '@brickflow/http'
|
|
241
|
+
1. Базовые настройки endpoint-а при объявлении:
|
|
255
242
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
243
|
+
```ts
|
|
244
|
+
const products = createGet<{ limit: number }>()('/products', {
|
|
245
|
+
mapParams: (params) => ({
|
|
246
|
+
limit: params?.limit ?? 10,
|
|
259
247
|
}),
|
|
260
|
-
)
|
|
248
|
+
})
|
|
261
249
|
```
|
|
262
250
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
Standalone client supports a dynamic resolver:
|
|
251
|
+
2. Runtime-опции при вызове:
|
|
266
252
|
|
|
267
253
|
```ts
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
254
|
+
const productsHttp = await products({
|
|
255
|
+
initParams: { limit: 20 },
|
|
256
|
+
lazy: true,
|
|
257
|
+
server: false,
|
|
272
258
|
})
|
|
273
259
|
```
|
|
274
260
|
|
|
275
|
-
|
|
261
|
+
Если `effect`, `mapParams` или `isError` указаны и при объявлении, и при вызове, пакет объединяет их так:
|
|
262
|
+
|
|
263
|
+
- `effect` вызывает оба обработчика
|
|
264
|
+
- `isError` из runtime имеет приоритет
|
|
265
|
+
- `mapParams` из runtime имеет приоритет
|
|
266
|
+
|
|
267
|
+
## createUseCase
|
|
268
|
+
|
|
269
|
+
В playground доменный модуль собирается через `createUseCase`:
|
|
276
270
|
|
|
277
271
|
```ts
|
|
278
|
-
import {
|
|
272
|
+
import { createUseCase } from '@brickflow/http'
|
|
279
273
|
|
|
280
|
-
|
|
281
|
-
const apiBaseUrl = useState('api-base-url', () => 'https://dummyjson.com')
|
|
274
|
+
import { createGet } from '~/core/http'
|
|
282
275
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
276
|
+
export default createUseCase()(({ httpClient }) => {
|
|
277
|
+
return {
|
|
278
|
+
async apiError() {
|
|
279
|
+
const { data } = await httpClient.post('/api/http-error-demo')
|
|
280
|
+
return data
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
products: createGet<{
|
|
284
|
+
limit: number
|
|
285
|
+
}>()('/products'),
|
|
286
|
+
}
|
|
288
287
|
})
|
|
289
288
|
```
|
|
290
289
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
```ts
|
|
294
|
-
const products = await useHttp({
|
|
295
|
-
url: '/products',
|
|
296
|
-
})
|
|
290
|
+
Это даёт простой контракт для DI: use-case получает `httpClient`, а внутри может смешивать:
|
|
297
291
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
```
|
|
292
|
+
- прямые mutation/one-shot запросы через `httpClient`
|
|
293
|
+
- stateful GET-запросы через `createGet`
|
|
301
294
|
|
|
302
|
-
|
|
295
|
+
## Использование в компоненте
|
|
303
296
|
|
|
304
|
-
|
|
305
|
-
- local proxy `/api/dummyjson`
|
|
297
|
+
Актуальный пример из `apps/playground/pages/index.vue`:
|
|
306
298
|
|
|
307
|
-
|
|
299
|
+
```vue
|
|
300
|
+
<script lang="ts" setup>
|
|
301
|
+
const app = useNuxtApp()
|
|
302
|
+
const httpProducts = await app.$di.product.products({
|
|
303
|
+
lazy: true,
|
|
304
|
+
})
|
|
305
|
+
</script>
|
|
306
|
+
|
|
307
|
+
<template>
|
|
308
|
+
<button
|
|
309
|
+
type="button"
|
|
310
|
+
@click="httpProducts.fetch()"
|
|
311
|
+
>
|
|
312
|
+
{{ httpProducts.hasFirstData }} Action
|
|
313
|
+
</button>
|
|
314
|
+
|
|
315
|
+
{{ httpProducts.data }}
|
|
316
|
+
</template>
|
|
317
|
+
```
|
|
308
318
|
|
|
309
|
-
|
|
310
|
-
- [apps/playground/app/plugins/http-middleware.ts](/Users/andrii/Lab/personal/brickme/apps/playground/app/plugins/http-middleware.ts:1)
|
|
319
|
+
Типичный сценарий:
|
|
311
320
|
|
|
312
|
-
|
|
321
|
+
1. Создать request-state через `await endpoint({ ...options })`
|
|
322
|
+
2. Если `lazy: true`, вызвать `fetch()` вручную
|
|
323
|
+
3. Читать `data`, `error`, `pending`, `pendingCache`
|
|
313
324
|
|
|
314
|
-
|
|
325
|
+
Пример с параметрами:
|
|
315
326
|
|
|
316
327
|
```ts
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
328
|
+
const productsHttp = await app.$di.product.products({
|
|
329
|
+
initParams: {
|
|
330
|
+
limit: 10,
|
|
331
|
+
},
|
|
332
|
+
lazy: true,
|
|
333
|
+
})
|
|
323
334
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
console.error('HTTP error', request.url, response.status)
|
|
327
|
-
}
|
|
328
|
-
})
|
|
335
|
+
await productsHttp.fetch({
|
|
336
|
+
limit: 30,
|
|
329
337
|
})
|
|
330
338
|
```
|
|
331
339
|
|
|
332
|
-
|
|
340
|
+
## Cache и синхронизация между вкладками
|
|
333
341
|
|
|
334
|
-
|
|
335
|
-
- `url`
|
|
336
|
-
- `method`
|
|
337
|
-
- `headers`
|
|
338
|
-
- `body`
|
|
339
|
-
- `params`
|
|
340
|
-
- `signal`
|
|
341
|
-
- `credentials`
|
|
342
|
+
Если передан `getCache`, `createUseHttp`:
|
|
342
343
|
|
|
343
|
-
|
|
344
|
+
- сначала пробует отдать cache
|
|
345
|
+
- вызывает `effect(..., { cached: true })` для cache-ответа
|
|
346
|
+
- затем делает сетевой запрос
|
|
347
|
+
- сохраняет успешный ответ в cache
|
|
348
|
+
- при изменении хеша удаляет связанные cache-ключи через `deleteKeysWithPart`
|
|
344
349
|
|
|
345
|
-
|
|
346
|
-
- `request`
|
|
350
|
+
Дополнительно пакет использует `BroadcastChannel` и синхронизирует свежие успешные GET-ответы между вкладками.
|
|
347
351
|
|
|
348
|
-
|
|
352
|
+
По умолчанию:
|
|
349
353
|
|
|
350
|
-
|
|
351
|
-
|
|
354
|
+
- `ttl = 7 дней`
|
|
355
|
+
- `channelName = 'http-tab-sync'`
|
|
352
356
|
|
|
353
|
-
|
|
354
|
-
request.headers.set('X-Debug', '1')
|
|
355
|
-
}
|
|
357
|
+
## SSR
|
|
356
358
|
|
|
357
|
-
|
|
358
|
-
removeHttpRequestMiddleware(middleware)
|
|
359
|
-
```
|
|
359
|
+
Если вызвать endpoint с `server: true`, на сервере пакет использует `useLazyAsyncData`, сохраняет результат в `useState` и переиспользует его на клиенте после гидрации.
|
|
360
360
|
|
|
361
|
-
|
|
361
|
+
Это полезно для страниц, где нужен первый ответ уже в SSR, но тот же контракт `data / pending / fetch` должен остаться и на клиенте.
|
|
362
362
|
|
|
363
|
-
|
|
363
|
+
## Вспомогательные функции
|
|
364
364
|
|
|
365
365
|
```ts
|
|
366
|
-
import {
|
|
366
|
+
import { createURL, hashData } from '@brickflow/http'
|
|
367
|
+
```
|
|
367
368
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
requestTimeoutMs: 10000,
|
|
371
|
-
retry: {
|
|
372
|
-
delay: 250,
|
|
373
|
-
retries: 2,
|
|
374
|
-
},
|
|
375
|
-
})
|
|
369
|
+
- `createURL(url, params)` строит query string
|
|
370
|
+
- `hashData(data)` считает SHA-1 через `crypto.subtle`, а без него использует fallback-хеш
|
|
376
371
|
|
|
377
|
-
|
|
378
|
-
```
|
|
372
|
+
## Когда что использовать
|
|
379
373
|
|
|
380
|
-
|
|
374
|
+
`createHttp`:
|
|
381
375
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
createHeaders: async () => {
|
|
386
|
-
return {
|
|
387
|
-
Authorization: `Bearer ${token}`,
|
|
388
|
-
}
|
|
389
|
-
},
|
|
390
|
-
})
|
|
391
|
-
```
|
|
376
|
+
- POST/мутации
|
|
377
|
+
- one-shot запросы
|
|
378
|
+
- когда не нужен reactive state
|
|
392
379
|
|
|
393
|
-
|
|
380
|
+
`createUseHttp` + `defineGet`:
|
|
381
|
+
|
|
382
|
+
- SSR-friendly GET
|
|
383
|
+
- cache и `pendingCache`
|
|
384
|
+
- повторное использование endpoint-описаний
|
|
385
|
+
- ручной `fetch()`
|
|
386
|
+
|
|
387
|
+
`createUseCase`:
|
|
394
388
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
- default Nuxt module
|
|
398
|
-
- `createHttpClient`
|
|
399
|
-
- `createTypedHttpClient`
|
|
400
|
-
- `createStrictHttpClient`
|
|
401
|
-
- `defineHttpRoutes`
|
|
402
|
-
- `addHttpRequestMiddleware`
|
|
403
|
-
- `addHttpResponseMiddleware`
|
|
404
|
-
- `removeHttpRequestMiddleware`
|
|
405
|
-
- `removeHttpResponseMiddleware`
|
|
406
|
-
- public TypeScript types for client, config, middleware, payloads, and typed route maps
|
|
389
|
+
- доменные модули с DI
|
|
390
|
+
- единый контракт для доступа к `httpClient`
|