@alistt69/create-api 0.2.11 → 0.4.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/README.md +328 -101
- package/dist/controller.d.ts +3 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +2 -0
- package/dist/controller.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/createApi.d.ts +1 -1
- package/dist/lib/createApi.d.ts.map +1 -1
- package/dist/lib/createApi.js +22 -2
- package/dist/lib/createApi.js.map +1 -1
- package/dist/lib/createController.d.ts +4 -0
- package/dist/lib/createController.d.ts.map +1 -0
- package/dist/lib/createController.js +113 -0
- package/dist/lib/createController.js.map +1 -0
- package/dist/lib/createRequestId.d.ts +2 -0
- package/dist/lib/createRequestId.d.ts.map +1 -0
- package/dist/lib/createRequestId.js +6 -0
- package/dist/lib/createRequestId.js.map +1 -0
- package/dist/lib/fetchBaseQuery.d.ts +1 -1
- package/dist/lib/fetchBaseQuery.d.ts.map +1 -1
- package/dist/lib/getHookName.d.ts +1 -1
- package/dist/lib/getHookName.d.ts.map +1 -1
- package/dist/lib/initiateMutation.d.ts +7 -0
- package/dist/lib/initiateMutation.d.ts.map +1 -0
- package/dist/lib/initiateMutation.js +47 -0
- package/dist/lib/initiateMutation.js.map +1 -0
- package/dist/lib/initiateQuery.d.ts +4 -0
- package/dist/lib/initiateQuery.d.ts.map +1 -0
- package/dist/lib/initiateQuery.js +35 -0
- package/dist/lib/initiateQuery.js.map +1 -0
- package/dist/lib/makeLazyQueryHook.d.ts +8 -8
- package/dist/lib/makeLazyQueryHook.d.ts.map +1 -1
- package/dist/lib/makeLazyQueryHook.js +10 -87
- package/dist/lib/makeLazyQueryHook.js.map +1 -1
- package/dist/lib/makeMutationHook.d.ts +1 -1
- package/dist/lib/makeMutationHook.d.ts.map +1 -1
- package/dist/lib/makeMutationHook.js +2 -6
- package/dist/lib/makeMutationHook.js.map +1 -1
- package/dist/lib/makeQueryHook.d.ts +9 -8
- package/dist/lib/makeQueryHook.d.ts.map +1 -1
- package/dist/lib/makeQueryHook.js +8 -79
- package/dist/lib/makeQueryHook.js.map +1 -1
- package/dist/lib/runQuery.d.ts +11 -0
- package/dist/lib/runQuery.d.ts.map +1 -0
- package/dist/lib/runQuery.js +83 -0
- package/dist/lib/runQuery.js.map +1 -0
- package/dist/model/mutation.d.ts +1 -1
- package/dist/model/mutation.d.ts.map +1 -1
- package/dist/model/query.d.ts +1 -1
- package/dist/model/query.d.ts.map +1 -1
- package/dist/model/queryStore.d.ts +10 -5
- package/dist/model/queryStore.d.ts.map +1 -1
- package/dist/model/queryStore.js +46 -8
- package/dist/model/queryStore.js.map +1 -1
- package/dist/model/types.d.ts +63 -6
- package/dist/model/types.d.ts.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<table width="100%">
|
|
2
2
|
<tr>
|
|
3
3
|
<td width="190" align="center">
|
|
4
|
-
<img src="assets/alistt69-packages-logo.svg" alt="
|
|
4
|
+
<img src="assets/alistt69-packages-logo.svg" alt="@alistt69 packages logo" width="160" height="160" />
|
|
5
5
|
</td>
|
|
6
6
|
<td>
|
|
7
7
|
<h1>@alistt69/create-api</h1>
|
|
8
8
|
|
|
9
9
|
> **One helper. No extra.**
|
|
10
|
-
> A lightweight createApi inspired by **RTKQ** — with query, lazy query and mutation hooks, built-in cache utilities, stale data handling and a tiny, focused API.
|
|
10
|
+
> A lightweight createApi inspired by **RTKQ** — with query, lazy query and mutation hooks & endpoint controllers, built-in cache utilities, stale data handling and a tiny, focused API.
|
|
11
11
|
|
|
12
12
|
[](https://www.npmjs.com/package/@alistt69/create-api)
|
|
13
13
|
[](https://nodejs.org/)
|
|
@@ -19,155 +19,382 @@
|
|
|
19
19
|
</tr>
|
|
20
20
|
</table>
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
---
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
<
|
|
26
|
-
|
|
24
|
+
<p align="center">
|
|
25
|
+
<a href="#install">Install</a>
|
|
26
|
+
· <a href="#quick-start">Quick Start</a>
|
|
27
|
+
· <a href="#the-shape">The Shape</a>
|
|
28
|
+
· <a href="#cache">Cache</a>
|
|
29
|
+
· <a href="#imperative-api">Imperative API</a>
|
|
30
|
+
· <a href="#controller">Controller</a>
|
|
31
|
+
</p>
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
---
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
## Why this exists
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
`@alistt69/create-api` is for React projects that like the ergonomics of
|
|
38
|
+
RTK Query, but do not want to introduce Redux just to fetch data.
|
|
33
39
|
|
|
34
|
-
|
|
40
|
+
It gives you a compact `createApi` workflow:
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
| Lazy query hooks | Trigger queries manually when needed |
|
|
40
|
-
| Mutation hooks | Handle writes with generated hooks |
|
|
41
|
-
| fetchBaseQuery | Ready-to-use HTTP baseQuery built on fetch |
|
|
42
|
-
| Cache utils | Read and patch cached data manually |
|
|
43
|
-
| Stale time support | Reuse cached data before refetching |
|
|
44
|
-
| Keep unused data for | Control cache lifetime after unmount |
|
|
45
|
-
|
|
46
|
-
## 🎯 Why use it?
|
|
47
|
-
|
|
48
|
-
This package is specified for projects with no Redux.
|
|
42
|
+
```txt
|
|
43
|
+
define endpoints -> get typed hooks -> read/write cache -> refetch when stale
|
|
44
|
+
```
|
|
49
45
|
|
|
50
|
-
|
|
46
|
+
Use it when you want:
|
|
51
47
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
| You need | You get |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| Generated React hooks | `useGetPostQuery`, `useLazyGetPostQuery`, `useUpdatePostMutation` |
|
|
51
|
+
| A small HTTP layer | `fetchBaseQuery`, built on native `fetch` |
|
|
52
|
+
| Cache reads and patches | `getQueryData`, `setQueryData`, `updateQueryData` |
|
|
53
|
+
| Stale data handling | `staleTime`, `keepUnusedDataFor`, `refetchOnMount` |
|
|
54
|
+
| Manual orchestration | `api.endpoints.*.initiate`, `select`, `subscribe` |
|
|
55
|
+
| Store-based usage | `createController` from the controller subpath |
|
|
58
56
|
|
|
59
|
-
##
|
|
60
|
-
* Node.js 18 or higher
|
|
61
|
-
* React 16.8 or higher
|
|
57
|
+
## Install
|
|
62
58
|
|
|
63
|
-
## 🔥 Install
|
|
64
59
|
```bash
|
|
65
60
|
npm i @alistt69/create-api
|
|
66
61
|
```
|
|
67
62
|
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
Requirements:
|
|
64
|
+
|
|
65
|
+
| Runtime | Version |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| Node.js | `>=18` |
|
|
68
|
+
| React | `>=16.8` |
|
|
69
|
+
|
|
70
|
+
## Quick Start
|
|
71
|
+
|
|
72
|
+
Create an API once:
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
70
75
|
import { createApi, fetchBaseQuery } from '@alistt69/create-api';
|
|
71
76
|
|
|
72
77
|
const api = createApi({
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
baseQuery: fetchBaseQuery({
|
|
79
|
+
baseUrl: 'https://example.com/api',
|
|
80
|
+
}),
|
|
81
|
+
|
|
82
|
+
endpoints: (builder) => ({
|
|
83
|
+
getPost: builder.query({
|
|
84
|
+
query: (id: string) => ({
|
|
85
|
+
url: `/posts/${id}`,
|
|
86
|
+
}),
|
|
75
87
|
}),
|
|
76
88
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
updatePost: builder.mutation({
|
|
85
|
-
query: ({ id, title }) => ({
|
|
86
|
-
url: `/posts/${id}`,
|
|
87
|
-
method: 'PATCH',
|
|
88
|
-
body: { title },
|
|
89
|
-
}),
|
|
90
|
-
}),
|
|
89
|
+
updatePost: builder.mutation({
|
|
90
|
+
query: ({ id, title }: { id: string; title: string }) => ({
|
|
91
|
+
url: `/posts/${id}`,
|
|
92
|
+
method: 'PATCH',
|
|
93
|
+
body: { title },
|
|
94
|
+
}),
|
|
91
95
|
}),
|
|
96
|
+
}),
|
|
92
97
|
});
|
|
93
98
|
```
|
|
94
99
|
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
Then use the generated hooks:
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
97
103
|
function Post() {
|
|
98
|
-
const { data, isLoading } = api.useGetPostQuery('1');
|
|
99
|
-
const [updatePost] = api.useUpdatePostMutation();
|
|
104
|
+
const { data, isLoading, refetch } = api.useGetPostQuery('1');
|
|
105
|
+
const [updatePost, updateState] = api.useUpdatePostMutation();
|
|
100
106
|
|
|
101
|
-
if (isLoading)
|
|
107
|
+
if (isLoading) {
|
|
108
|
+
return <div>Loading...</div>;
|
|
109
|
+
}
|
|
102
110
|
|
|
103
111
|
return (
|
|
104
|
-
<
|
|
105
|
-
{data?.title}
|
|
106
|
-
|
|
112
|
+
<section>
|
|
113
|
+
<h2>{data?.title}</h2>
|
|
114
|
+
|
|
115
|
+
<button
|
|
116
|
+
disabled={updateState.isLoading}
|
|
117
|
+
onClick={() => updatePost({ id: '1', title: 'Updated' })}
|
|
118
|
+
>
|
|
119
|
+
Update
|
|
120
|
+
</button>
|
|
121
|
+
|
|
122
|
+
<button onClick={() => refetch()}>
|
|
123
|
+
Refetch
|
|
124
|
+
</button>
|
|
125
|
+
</section>
|
|
107
126
|
);
|
|
108
127
|
}
|
|
109
128
|
```
|
|
110
129
|
|
|
111
|
-
##
|
|
130
|
+
## The Shape
|
|
131
|
+
|
|
132
|
+
The package is intentionally small, but the surface area covers the core
|
|
133
|
+
data-fetching loop.
|
|
134
|
+
|
|
135
|
+
### Query hooks
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
const result = api.useGetPostQuery('1', {
|
|
139
|
+
enabled: true,
|
|
140
|
+
refetchOnMount: true,
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Query state includes:
|
|
145
|
+
|
|
146
|
+
| Field | Meaning |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| `data` | Last successful value |
|
|
149
|
+
| `error` | Last failed value |
|
|
150
|
+
| `status` | `uninitialized`, `pending`, `fulfilled` or `rejected` |
|
|
151
|
+
| `isLoading` | First request is in progress |
|
|
152
|
+
| `isFetching` | Any request is in progress, including background refetch |
|
|
153
|
+
| `isSuccess` | Last known state is successful |
|
|
154
|
+
| `isError` | Last known state is failed |
|
|
155
|
+
| `fulfilledAt` | Timestamp of the last fulfilled request |
|
|
156
|
+
| `requestId` | Active or last request id |
|
|
157
|
+
|
|
158
|
+
### Lazy query hooks
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
const [loadPost, post] = api.useLazyGetPostQuery();
|
|
162
|
+
|
|
163
|
+
await loadPost('1');
|
|
164
|
+
|
|
165
|
+
post.refetch();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Lazy queries are useful when the request should start from a user action,
|
|
169
|
+
a modal opening, a route transition or another explicit event.
|
|
112
170
|
|
|
113
|
-
|
|
171
|
+
### Mutation hooks
|
|
114
172
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
- `validateStatus`
|
|
123
|
-
- `fetchFn`
|
|
173
|
+
```tsx
|
|
174
|
+
const [updatePost, updateState] = api.useUpdatePostMutation();
|
|
175
|
+
|
|
176
|
+
await updatePost({ id: '1', title: 'Updated' });
|
|
177
|
+
|
|
178
|
+
updateState.reset();
|
|
179
|
+
```
|
|
124
180
|
|
|
125
|
-
|
|
181
|
+
Mutation state tracks the latest trigger. This keeps UI behavior predictable
|
|
182
|
+
when several mutation calls overlap.
|
|
183
|
+
|
|
184
|
+
## `fetchBaseQuery`
|
|
185
|
+
|
|
186
|
+
`fetchBaseQuery` is a ready-to-use `baseQuery` built on top of native `fetch`.
|
|
187
|
+
It handles URLs, params, JSON bodies, headers, timeouts and response parsing.
|
|
126
188
|
|
|
127
189
|
```tsx
|
|
128
190
|
import { createApi, fetchBaseQuery } from '@alistt69/create-api';
|
|
129
191
|
|
|
130
192
|
const api = createApi({
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
193
|
+
baseQuery: fetchBaseQuery({
|
|
194
|
+
baseUrl: 'https://example.com/api',
|
|
195
|
+
timeout: 10_000,
|
|
196
|
+
prepareHeaders: (headers) => {
|
|
197
|
+
headers.set('authorization', 'Bearer token');
|
|
198
|
+
return headers;
|
|
199
|
+
},
|
|
200
|
+
}),
|
|
201
|
+
|
|
202
|
+
endpoints: (builder) => ({
|
|
203
|
+
getTickets: builder.query({
|
|
204
|
+
query: ({ page }: { page: number }) => ({
|
|
205
|
+
url: '/tickets',
|
|
206
|
+
params: { page },
|
|
207
|
+
}),
|
|
145
208
|
}),
|
|
209
|
+
}),
|
|
146
210
|
});
|
|
147
211
|
```
|
|
148
212
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
213
|
+
Supported options:
|
|
214
|
+
|
|
215
|
+
| Option | Where | Purpose |
|
|
216
|
+
| --- | --- | --- |
|
|
217
|
+
| `baseUrl` | base query | Prefix all request URLs |
|
|
218
|
+
| `headers` | request | Add request-specific headers |
|
|
219
|
+
| `prepareHeaders` | base query | Modify headers before every request |
|
|
220
|
+
| `params` | request | Append query params |
|
|
221
|
+
| `paramsSerializer` | base query | Customize query string serialization |
|
|
222
|
+
| `body` | request | Send JSON, `FormData`, `Blob`, `URLSearchParams` or another fetch body |
|
|
223
|
+
| `timeout` | both | Abort slow requests |
|
|
224
|
+
| `responseHandler` | both | Parse as `json`, `text`, `content-type` or custom handler |
|
|
225
|
+
| `validateStatus` | both | Decide whether a response is success |
|
|
226
|
+
| `fetchFn` | base query | Use a custom fetch implementation |
|
|
227
|
+
|
|
228
|
+
Custom response handling:
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
downloadReport: builder.query({
|
|
232
|
+
query: () => ({
|
|
233
|
+
url: '/report',
|
|
234
|
+
responseHandler: (response) => response.blob(),
|
|
235
|
+
}),
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Cache
|
|
240
|
+
|
|
241
|
+
Queries are cached by endpoint name and serialized argument.
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
const api = createApi({
|
|
245
|
+
baseQuery,
|
|
246
|
+
endpoints: (builder) => ({
|
|
247
|
+
getTicketById: builder.query({
|
|
248
|
+
query: (id: string) => ({ url: `/tickets/${id}` }),
|
|
249
|
+
serializeArgs: (id) => id,
|
|
250
|
+
staleTime: 2_000,
|
|
251
|
+
keepUnusedDataFor: 10_000,
|
|
155
252
|
}),
|
|
156
|
-
}),
|
|
253
|
+
}),
|
|
254
|
+
});
|
|
157
255
|
```
|
|
158
256
|
|
|
159
|
-
|
|
160
|
-
|
|
257
|
+
Cache controls:
|
|
258
|
+
|
|
259
|
+
| Option | Behavior |
|
|
260
|
+
| --- | --- |
|
|
261
|
+
| `serializeArgs` | Builds the cache key for an endpoint argument |
|
|
262
|
+
| `staleTime` | Keeps fulfilled data fresh for automatic mount behavior |
|
|
263
|
+
| `keepUnusedDataFor` | Keeps unused cache alive after the last subscriber leaves |
|
|
264
|
+
| `refetchOnMount` | Controls whether cached data may refetch on mount |
|
|
265
|
+
| `enabled` | Disables automatic query execution while keeping manual `refetch` available |
|
|
266
|
+
|
|
267
|
+
Manual cache updates:
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
161
270
|
api.util.getQueryData('getPost', '1');
|
|
162
|
-
|
|
271
|
+
|
|
272
|
+
api.util.setQueryData('getPost', '1', {
|
|
273
|
+
id: '1',
|
|
274
|
+
title: 'Local title',
|
|
275
|
+
});
|
|
276
|
+
|
|
163
277
|
api.util.updateQueryData('getPost', '1', (prev) => ({
|
|
164
|
-
|
|
165
|
-
|
|
278
|
+
...prev,
|
|
279
|
+
title: 'Patched title',
|
|
166
280
|
}));
|
|
167
281
|
```
|
|
168
282
|
|
|
169
|
-
|
|
283
|
+
### Invalidation
|
|
284
|
+
|
|
285
|
+
Use endpoint-level invalidation for broad refetching:
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
editTicket: builder.mutation({
|
|
289
|
+
query: ({ id, title }) => ({
|
|
290
|
+
url: `/tickets/${id}`,
|
|
291
|
+
method: 'PATCH',
|
|
292
|
+
body: { title },
|
|
293
|
+
}),
|
|
294
|
+
invalidates: ['getTickets'],
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Use tag invalidation when you want to target specific cached records:
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
getTicketById: builder.query({
|
|
302
|
+
query: (id) => ({ url: `/tickets/${id}` }),
|
|
303
|
+
providesTags: (_result, id) => [`Ticket/${id}`],
|
|
304
|
+
}),
|
|
305
|
+
|
|
306
|
+
editTicket: builder.mutation({
|
|
307
|
+
query: ({ id, title }) => ({
|
|
308
|
+
url: `/tickets/${id}`,
|
|
309
|
+
method: 'PATCH',
|
|
310
|
+
body: { title },
|
|
311
|
+
}),
|
|
312
|
+
invalidatesTags: (_result, arg) => [`Ticket/${arg.id}`],
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Imperative API
|
|
317
|
+
|
|
318
|
+
Every endpoint also exposes a small imperative API. It is useful when a query
|
|
319
|
+
lifecycle should live outside React components.
|
|
320
|
+
|
|
321
|
+
```tsx
|
|
322
|
+
const request = api.endpoints.getPost.initiate('1');
|
|
323
|
+
|
|
324
|
+
const data = await request.unwrap();
|
|
325
|
+
|
|
326
|
+
await request.refetch();
|
|
327
|
+
|
|
328
|
+
request.unsubscribe();
|
|
329
|
+
request.abort();
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
You can also inspect query state directly:
|
|
333
|
+
|
|
334
|
+
```tsx
|
|
335
|
+
const state = api.endpoints.getPost.select('1');
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
And trigger mutations:
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
const mutation = api.endpoints.updatePost.initiate({
|
|
342
|
+
id: '1',
|
|
343
|
+
title: 'Updated',
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
await mutation.unwrap();
|
|
347
|
+
|
|
348
|
+
mutation.abort();
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Controller
|
|
352
|
+
|
|
353
|
+
For store-based usage, import `createController` from the controller subpath:
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
import { createController } from '@alistt69/create-api/controller';
|
|
357
|
+
|
|
358
|
+
class TicketStore {
|
|
359
|
+
private ticket = createController(api.endpoints.getTicketById);
|
|
360
|
+
private editTicket = createController(api.endpoints.editTicket);
|
|
361
|
+
|
|
362
|
+
load(id: string) {
|
|
363
|
+
return this.ticket.run(id);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
save(id: string, title: string) {
|
|
367
|
+
return this.editTicket.run({ id, title });
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
get ticketData() {
|
|
371
|
+
return this.ticket.state.data;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
get isLoadingTicket() {
|
|
375
|
+
return this.ticket.state.isLoading;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
get isSavingTicket() {
|
|
379
|
+
return this.editTicket.state.isLoading;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
destroy() {
|
|
383
|
+
this.ticket.dispose();
|
|
384
|
+
this.editTicket.dispose();
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
The controller keeps endpoint state outside React, subscribes to cache updates,
|
|
390
|
+
and releases its subscription with `dispose()`.
|
|
391
|
+
|
|
392
|
+
## Demo
|
|
393
|
+
|
|
394
|
+
[Live demo](https://create-api-demo.vercel.app/)
|
|
395
|
+
|
|
396
|
+
Sandbox is coming soon.
|
|
170
397
|
|
|
171
|
-
|
|
398
|
+
## License
|
|
172
399
|
|
|
173
|
-
|
|
400
|
+
MIT. See [LICENSE](./LICENSE).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,YAAY,EACR,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,GAClB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export {
|
|
1
|
+
export { createApi } from './lib/createApi.js';
|
|
2
|
+
export { fetchBaseQuery } from './lib/fetchBaseQuery.js';
|
|
3
|
+
export type { BaseQueryFn, BaseQueryResult, FetchBaseQueryArgs, FetchBaseQueryError, FetchBaseQueryMeta, FetchBaseQueryOptions, InferQueryState, QueryHookOptions, QueryInitiateResult, TagDescription, } from './model/types.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,YAAY,EACR,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,GACjB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export { createApi, fetchBaseQuery };
|
|
1
|
+
export { createApi } from './lib/createApi.js';
|
|
2
|
+
export { fetchBaseQuery } from './lib/fetchBaseQuery.js';
|
|
4
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/lib/createApi.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mutation } from '../model/mutation.js';
|
|
2
2
|
import { query } from '../model/query.js';
|
|
3
|
-
import { BaseQueryFn, CreateApiResult, GeneralDefinition } from '../model/types.js';
|
|
3
|
+
import { type BaseQueryFn, type CreateApiResult, type GeneralDefinition } from '../model/types.js';
|
|
4
4
|
export interface BaseQueryArgs {
|
|
5
5
|
url: string;
|
|
6
6
|
body?: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createApi.d.ts","sourceRoot":"","sources":["../../src/lib/createApi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"createApi.d.ts","sourceRoot":"","sources":["../../src/lib/createApi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAY1C,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAsB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AASvH,MAAM,WAAW,aAAa;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,UAAU,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACjE,SAAS,EAAE,WAAW,CAAC;IACvB,SAAS,EAAE,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,OAAO,KAAK,CAAC;QAAC,QAAQ,EAAE,OAAO,QAAQ,CAAA;KAAE,KAAK,CAAC,CAAC;CACjF;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAAE,EACnE,SAAS,EACT,SAAS,GACZ,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAwEzC"}
|
package/dist/lib/createApi.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { mutation } from '../model/mutation.js';
|
|
2
2
|
import { query } from '../model/query.js';
|
|
3
|
-
import { getQueryData, setQueryData,
|
|
3
|
+
import { getQueryData, getQueryKeyByEndpointArg, invalidateTags, selectQueryState, setQueryData, setQueryKeySerializer, setQueryTagResolver, subscribeToQuery, updateQueryData, } from '../model/queryStore.js';
|
|
4
4
|
import { getHookName } from './getHookName.js';
|
|
5
|
+
import { initiateQuery } from './initiateQuery.js';
|
|
5
6
|
import { makeLazyQueryHook } from './makeLazyQueryHook.js';
|
|
6
7
|
import { makeMutationHook } from './makeMutationHook.js';
|
|
7
8
|
import { makeQueryHook } from './makeQueryHook.js';
|
|
8
9
|
import { typedObjectKeys } from './typedObjectKeys.js';
|
|
10
|
+
import { initiateMutation } from './initiateMutation.js';
|
|
9
11
|
export function createApi({ endpoints, baseQuery, }) {
|
|
10
12
|
const transformedEndpoints = endpoints({ query, mutation });
|
|
11
13
|
const keys = typedObjectKeys(transformedEndpoints);
|
|
12
14
|
const apiResult = {};
|
|
15
|
+
const localEndpoints = {};
|
|
13
16
|
keys.forEach((endpointName) => {
|
|
14
17
|
const definition = transformedEndpoints[endpointName];
|
|
15
18
|
const makeHookProps = {
|
|
@@ -17,6 +20,22 @@ export function createApi({ endpoints, baseQuery, }) {
|
|
|
17
20
|
endpointName,
|
|
18
21
|
...definition,
|
|
19
22
|
};
|
|
23
|
+
if (definition.type === 'query') {
|
|
24
|
+
localEndpoints[endpointName] = {
|
|
25
|
+
name: endpointName,
|
|
26
|
+
type: definition.type,
|
|
27
|
+
select: (arg) => selectQueryState(endpointName, arg),
|
|
28
|
+
initiate: (arg) => initiateQuery(makeHookProps, arg),
|
|
29
|
+
subscribe: (arg, listener) => subscribeToQuery(getQueryKeyByEndpointArg(endpointName, arg), listener, definition.keepUnusedDataFor ?? 0),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (definition.type === 'mutation') {
|
|
33
|
+
localEndpoints[endpointName] = {
|
|
34
|
+
name: endpointName,
|
|
35
|
+
type: definition.type,
|
|
36
|
+
initiate: (arg) => initiateMutation(makeHookProps, arg),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
20
39
|
if (definition.type === 'query') {
|
|
21
40
|
setQueryKeySerializer(endpointName, (arg) => definition.serializeArgs
|
|
22
41
|
? definition.serializeArgs(arg)
|
|
@@ -33,7 +52,8 @@ export function createApi({ endpoints, baseQuery, }) {
|
|
|
33
52
|
getQueryData,
|
|
34
53
|
setQueryData,
|
|
35
54
|
updateQueryData,
|
|
55
|
+
invalidateTags,
|
|
36
56
|
};
|
|
37
|
-
return { ...apiResult, util };
|
|
57
|
+
return { ...apiResult, endpoints: localEndpoints, util };
|
|
38
58
|
}
|
|
39
59
|
//# sourceMappingURL=createApi.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createApi.js","sourceRoot":"","sources":["../../src/lib/createApi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EACH,YAAY,EACZ,YAAY,EACZ,
|
|
1
|
+
{"version":3,"file":"createApi.js","sourceRoot":"","sources":["../../src/lib/createApi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type ApiEndpointMutation, type ApiEndpointQuery, type MutationController, type QueryController } from '../model/types.js';
|
|
2
|
+
export declare function createController<R, A>(endpoint: ApiEndpointQuery<R, A>): QueryController<R, A>;
|
|
3
|
+
export declare function createController<R, A>(endpoint: ApiEndpointMutation<R, A>): MutationController<R, A>;
|
|
4
|
+
//# sourceMappingURL=createController.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createController.d.ts","sourceRoot":"","sources":["../../src/lib/createController.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EAGrB,KAAK,kBAAkB,EAEvB,KAAK,eAAe,EAEvB,MAAM,mBAAmB,CAAC;AAE3B,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,EACjC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,GACjC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEzB,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,EACjC,QAAQ,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,GACpC,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC"}
|