@@ -55,15 +55,16 @@ to handle, modify, or pass-along a request.
55
55
56
56
``` ts
57
57
interface RequestManager {
58
- async request<T >(req : RequestInfo ): Future <T >;
58
+ request<T >(req : RequestInfo ): Future <T >;
59
59
}
60
60
```
61
61
62
62
63
63
For example:
64
64
65
65
``` ts
66
- import RequestManager , { Fetch } from ' @ember-data/request' ;
66
+ import { RequestManager } from ' @ember-data/request' ;
67
+ import { Fetch } from ' @ember/data/request/fetch' ;
67
68
import Auth from ' ember-simple-auth/ember-data-handler' ;
68
69
import Config from ' ./config' ;
69
70
@@ -99,42 +100,100 @@ interface Future<T> extends Promise<StructuredDocument<T>> {
99
100
}
100
101
```
101
102
102
- The ` StructuredDocument ` interface is the same as is proposed in RFC 854 and copied below:
103
+ The ` StructuredDocument ` interface is the same as is proposed in emberjs/rfcs # 854 but is shown here in richer detail.
103
104
104
105
``` ts
105
- interface StructuredDocument <T > {
106
- request: {
107
- url: string ;
108
- cache? : { key? : string , reload? : boolean , backgroundReload? : boolean };
109
- method: ' GET' | ' POST' | ' PUT' | ' DELETE' | ' PATCH' ;
110
- data? : Record <string , unknown >;
111
- options? : Record <string , unknown >;
112
- headers: Record <string , string >;
113
- }
114
- response: {
115
- status: HTTPStatusCode ;
116
- headers: Record <string , string >;
117
- }
106
+ interface RequestInfo {
107
+ /**
108
+ * data that a handler should convert into
109
+ * the query (GET) or body (POST)
110
+ */
111
+ data? : Record <string , unknown >;
112
+ /**
113
+ * options specifically intended for handlers
114
+ * to utilize to process the request
115
+ */
116
+ options? : Record <string , unknown >;
117
+ /**
118
+ * Allows supplying a custom AbortController for
119
+ * the request, if none is supplied one is generated
120
+ * for the request. When calling `next` if none is
121
+ * provided the primary controller for the request
122
+ * is used.
123
+ *
124
+ * controller will not be passed through onto the immutable
125
+ * request on the context supplied to handlers.
126
+ */
127
+ controller? : AbortController ;
128
+
129
+ // the below options perfectly mirror the
130
+ // native Request interface
131
+ cache? : RequestCache ;
132
+ credentials? : RequestCredentials ;
133
+ destination? : RequestDestination ;
134
+ /**
135
+ * Once a request has been made it becomes immutable, this
136
+ * includes Headers. To modify headers you may copy existing
137
+ * headers using `new Headers([...headers.entries()])`.
138
+ *
139
+ * Immutable headers instances have an additional method `clone`
140
+ * to allow this to be done swiftly.
141
+ */
142
+ headers? : Headers ;
143
+ integrity? : string ;
144
+ keepalive? : boolean ;
145
+ method? : string ;
146
+ mode? : RequestMode ;
147
+ redirect? : RequestRedirect ;
148
+ referrer? : string ;
149
+ referrerPolicy? : ReferrerPolicy ;
150
+ /**
151
+ * Typically you should not set this, though you may choose to curry
152
+ * a received signal if calling next. signal will automatically be set
153
+ * to the associated controller's signal if none is supplied.
154
+ */
155
+ signal? : AbortSignal ;
156
+ url? : string ;
157
+ }
158
+ interface ResponseInfo {
159
+ headers: Headers ;
160
+ ok: boolean ;
161
+ redirected: boolean ;
162
+ status: number ;
163
+ statusText: string ;
164
+ type: string ;
165
+ url: string ;
166
+ }
167
+
168
+ interface StructuredDataDocument <T > {
169
+ request: RequestInfo ;
170
+ response: ResponseInfo ;
118
171
data: T ;
119
- error? : Error ;
120
172
}
173
+ interface StructuredErrorDocument extends Error {
174
+ request: RequestInfo ;
175
+ response: ResponseInfo ;
176
+ error: string | object ;
177
+ }
178
+ type StructuredDocument <T > = StructuredDataDocument <T > | StructuredErrorDocument ;
121
179
```
122
180
181
+ A ` Future ` resolves with a StructuredDataDocument or rejects with a StructuredErrorDocument.
182
+
123
183
** Request Handlers**
124
184
125
185
Requests are fulfilled by handlers. A handler receives the request context
126
186
as well as a ` next ` function with which to pass along a request to the next
127
187
handler if it so chooses.
128
188
129
- If a handler calls ` next ` , it receives a ` Future ` which resolves to a ` StructuredDocument `
189
+ If a handler calls ` next ` , it receives a ` Future ` which fuulfills to a ` StructuredDocument `
130
190
that it can then compose how it sees fit with its own response.
131
191
132
192
``` ts
193
+ type NextFn = <P >(req : RequestInfo ) => Future <P >;
133
194
134
- type NextFn <P > = (req : RequestInfo ) => Future <P >;
135
-
136
- interface Handler <T > {
137
- async request(context : RequestContext , next : NextFn <P >): T ;
195
+ interface Handler {
196
+ request<T >(context : RequestContext , next : NextFn ): T ;
138
197
}
139
198
```
140
199
@@ -146,17 +205,8 @@ interface Handler<T> {
146
205
interface RequestContext <T > {
147
206
readonly request: RequestInfo ;
148
207
149
- setStream(stream : ReadableStream | Promise <ReadableStream >): void ;
150
- setResponse(response : Response ): void ;
151
- }
152
-
153
- interface RequestInfo {
154
- url: string ;
155
- method: ' GET' | ' POST' | ' PUT' | ' DELETE' | ' PATCH' ;
156
- data? : Record <string , unknown >;
157
- options? : Record <string , unknown >;
158
- headers: Record <string , string >;
159
- signal: AbortSignal ;
208
+ setStream(stream : ReadableStream | Promise <ReadableStream | null >): void ;
209
+ setResponse(response : ResponseInfo | Response | null ): void ;
160
210
}
161
211
```
162
212
@@ -264,10 +314,9 @@ Similarly, if `next` is called only a single time and neither `setStream` nor `g
264
314
called, we automatically curry the stream from the future returned by ` next ` onto the future returned by the handler.
265
315
266
316
Finally, if the return value of a handler is a ` Future ` , we curry the entire thing. This makes the
267
- following possible and ensures even ` data ` is curried when doing so: ` return next(<req>) ` .
317
+ following possible and ensures even ` data ` and ` error ` is curried when doing so: ` return next(<req>) ` .
268
318
269
- In the case of the ` Future ` being returned, ` Stream ` proxying is automatic and immediate and does
270
- not wait for the ` Future ` to resolve.
319
+ In the case of the ` Future ` being returned from a handler not using ` async/await ` , ` Stream ` proxying is automatic and immediate and does not wait for the ` Future ` to resolve. If the handler uses ` async/await ` we have no ability to detect the Future until the handler has fully resolved. This means that if using ` async/await ` in your handler you should always pro-actively pipe the stream.
271
320
272
321
** Using as a Service**
273
322
@@ -277,7 +326,8 @@ applications by exporting the manager as an Ember service.
277
326
278
327
* services/request.ts*
279
328
``` ts
280
- import RequestManager , { Fetch } from ' @ember-data/request' ;
329
+ import { RequestManager } from ' @ember-data/request' ;
330
+ import { Fetch } from ' @ember/data/request/fetch' ;
281
331
import Auth from ' ember-simple-auth/ember-data-handler' ;
282
332
283
333
export default class extends RequestManager {
@@ -306,7 +356,8 @@ Alternatively to have a request service unique to the store:
306
356
307
357
``` ts
308
358
import Store from ' @ember-data/store' ;
309
- import RequestManager , { Fetch } from ' @ember-data/request' ;
359
+ import { RequestManager } from ' @ember-data/request' ;
360
+ import { Fetch } from ' @ember/data/request/fetch' ;
310
361
311
362
export default class extends Store {
312
363
requestManager = new RequestManager ();
@@ -325,8 +376,8 @@ like the above would need to be done by the consuming application in order to ma
325
376
326
377
``` ts
327
378
import Store from ' @ember-data/store' ;
328
- import RequestManager from ' @ember-data/request' ;
329
- import LegacyHandler from ' @ember-data/legacy-network-handler' ;
379
+ import { RequestManager } from ' @ember-data/request' ;
380
+ import { LegacyHandler } from ' @ember-data/legacy-network-handler' ;
330
381
331
382
export default class extends Store {
332
383
requestManager = new RequestManager ();
@@ -344,7 +395,7 @@ The `Store` will add support for using the `RequestManager` via `store.request(<
344
395
345
396
``` ts
346
397
class Store {
347
- async request<T >(req : RequestInfo ): Future <Reified <T >>;
398
+ request<T >(req : RequestInfo ): Future <Reified <T >>;
348
399
}
349
400
```
350
401
@@ -388,6 +439,13 @@ interface StoreRequestInfo extends RequestInfo {
388
439
}
389
440
```
390
441
442
+ ** Background Reload Error Handling**
443
+
444
+ When an error occurs during a background request we will update the cache with the StructuredErrorDocument but will swallowed the Error at that point.
445
+
446
+ This prevents consuming applications from being required to catch the error unless
447
+ they wish to via a handler.
448
+
391
449
### RequestStateService
392
450
393
451
We do not intend to make any adjustments to the RequestStateService at this time, though
@@ -549,6 +607,15 @@ and feature-set that this shift brings will –over the course of the few years
549
607
removal– prove to users that the Adapter and Serializer world is no longer the best paradigm
550
608
for their applications.
551
609
610
+ ### Typescript Support
611
+
612
+ Although EmberData has not more broadly shipped support for Typescript, experimental types
613
+ will be shipped specifically for the RequestManager package. We can do this because the lack
614
+ of entanglement with the other packages affords us the ability to more safely ship this subset
615
+ of types while the others are still incomplete.
616
+
617
+ Types for other packages will eventually be provided but we will not rush them at this time.
618
+
552
619
## How we teach this
553
620
554
621
- EmberData should create new documentation and guides to cover using the RequestManager.
0 commit comments