@@ -205,8 +205,267 @@ fastify.listen(4000);
205
205
console .log (' Listening to port 4000' );
206
206
```
207
207
208
+ #### Use the client
209
+
210
+ ``` js
211
+ import { createClient } from ' graphql-http' ;
212
+
213
+ const client = createClient ({
214
+ url: ' http://localhost:4000/graphql' ,
215
+ });
216
+
217
+ (async () => {
218
+ let cancel = () => {
219
+ /* abort the request if it is in-flight */
220
+ };
221
+
222
+ const result = await new Promise ((resolve , reject ) => {
223
+ let result;
224
+ cancel = client .subscribe (
225
+ {
226
+ query: ' { hello }' ,
227
+ },
228
+ {
229
+ next : (data ) => (result = data),
230
+ error: reject,
231
+ complete : () => resolve (result),
232
+ },
233
+ );
234
+ });
235
+
236
+ expect (result).toEqual ({ hello: ' world' });
237
+ })();
238
+ ```
239
+
208
240
## Recipes
209
241
242
+ <details id =" promise " >
243
+ <summary ><a href =" #promise " >🔗</a > Client usage with Promise</summary >
244
+
245
+ ``` ts
246
+ import { ExecutionResult } from ' graphql' ;
247
+ import { createClient , RequestParams } from ' graphql-http' ;
248
+ import { getSession } from ' ./my-auth' ;
249
+
250
+ const client = createClient ({
251
+ url: ' http://hey.there:4000/graphql' ,
252
+ headers : async () => {
253
+ const session = await getSession ();
254
+ if (session ) {
255
+ return {
256
+ Authorization: ` Bearer ${session .token } ` ,
257
+ };
258
+ }
259
+ },
260
+ });
261
+
262
+ function execute<Data , Extensions >(
263
+ params : RequestParams ,
264
+ ): [request : Promise <ExecutionResult <Data , Extensions >>, cancel : () => void ] {
265
+ let cancel! : () => void ;
266
+ const request = new Promise <ExecutionResult <Data , Extensions >>(
267
+ (resolve , reject ) => {
268
+ let result: ExecutionResult <Data , Extensions >;
269
+ cancel = client .subscribe <Data , Extensions >(params , {
270
+ next : (data ) => (result = data ),
271
+ error: reject ,
272
+ complete : () => resolve (result ),
273
+ });
274
+ },
275
+ );
276
+ return [request , cancel ];
277
+ }
278
+
279
+ (async () => {
280
+ const [request, cancel] = execute ({
281
+ query: ' { hello }' ,
282
+ });
283
+
284
+ // just an example, not a real function
285
+ onUserLeavePage (() => {
286
+ cancel ();
287
+ });
288
+
289
+ const result = await request ;
290
+
291
+ expect (result ).toBe ({ data: { hello: ' world' } });
292
+ })();
293
+ ```
294
+
295
+ </details >
296
+
297
+ </details >
298
+
299
+ <details id =" observable " >
300
+ <summary ><a href =" #observable " >🔗</a > Client usage with <a href =" https://github.com/tc39/proposal-observable " >Observable</a ></summary >
301
+
302
+ ``` js
303
+ import { Observable } from ' relay-runtime' ;
304
+ // or
305
+ import { Observable } from ' @apollo/client/core' ;
306
+ // or
307
+ import { Observable } from ' rxjs' ;
308
+ // or
309
+ import Observable from ' zen-observable' ;
310
+ // or any other lib which implements Observables as per the ECMAScript proposal: https://github.com/tc39/proposal-observable
311
+ import { createClient } from ' graphql-http' ;
312
+ import { getSession } from ' ./my-auth' ;
313
+
314
+ const client = createClient ({
315
+ url: ' http://graphql.loves:4000/observables' ,
316
+ headers: async () => {
317
+ const session = await getSession ();
318
+ if (session) {
319
+ return {
320
+ Authorization: ` Bearer ${ session .token } ` ,
321
+ };
322
+ }
323
+ },
324
+ });
325
+
326
+ const observable = new Observable ((observer ) =>
327
+ client .subscribe ({ query: ' { hello }' }, observer),
328
+ );
329
+
330
+ const subscription = observable .subscribe ({
331
+ next : (result ) => {
332
+ expect (result).toBe ({ data: { hello: ' world' } });
333
+ },
334
+ });
335
+
336
+ // unsubscribe will cancel the request if it is pending
337
+ subscription .unsubscribe ();
338
+ ```
339
+
340
+ </details >
341
+
342
+ <details id =" relay " >
343
+ <summary ><a href =" #relay " >🔗</a > Client usage with <a href =" https://relay.dev " >Relay</a ></summary >
344
+
345
+ ``` ts
346
+ import { GraphQLError } from ' graphql' ;
347
+ import {
348
+ Network ,
349
+ Observable ,
350
+ RequestParameters ,
351
+ Variables ,
352
+ } from ' relay-runtime' ;
353
+ import { createClient } from ' graphql-http' ;
354
+ import { getSession } from ' ./my-auth' ;
355
+
356
+ const client = createClient ({
357
+ url: ' http://i.love:4000/graphql' ,
358
+ headers : async () => {
359
+ const session = await getSession ();
360
+ if (session ) {
361
+ return {
362
+ Authorization: ` Bearer ${session .token } ` ,
363
+ };
364
+ }
365
+ },
366
+ });
367
+
368
+ function fetch(operation : RequestParameters , variables : Variables ) {
369
+ return Observable .create ((sink ) => {
370
+ if (! operation .text ) {
371
+ return sink .error (new Error (' Operation text cannot be empty' ));
372
+ }
373
+ return client .subscribe (
374
+ {
375
+ operationName: operation .name ,
376
+ query: operation .text ,
377
+ variables ,
378
+ },
379
+ sink ,
380
+ );
381
+ });
382
+ }
383
+
384
+ export const network = Network .create (fetch );
385
+ ```
386
+
387
+ </details >
388
+
389
+ <details id =" apollo-client " >
390
+ <summary ><a href =" #apollo-client " >🔗</a > Client usage with <a href =" https://www.apollographql.com " >Apollo</a ></summary >
391
+
392
+ ``` ts
393
+ import {
394
+ ApolloLink ,
395
+ Operation ,
396
+ FetchResult ,
397
+ Observable ,
398
+ } from ' @apollo/client/core' ;
399
+ import { print , GraphQLError } from ' graphql' ;
400
+ import { createClient , ClientOptions , Client } from ' graphql-http' ;
401
+ import { getSession } from ' ./my-auth' ;
402
+
403
+ class HTTPLink extends ApolloLink {
404
+ private client: Client ;
405
+
406
+ constructor (options : ClientOptions ) {
407
+ super ();
408
+ this .client = createClient (options );
409
+ }
410
+
411
+ public request(operation : Operation ): Observable <FetchResult > {
412
+ return new Observable ((sink ) => {
413
+ return this .client .subscribe <FetchResult >(
414
+ { ... operation , query: print (operation .query ) },
415
+ {
416
+ next: sink .next .bind (sink ),
417
+ complete: sink .complete .bind (sink ),
418
+ error: sink .error .bind (sink ),
419
+ },
420
+ );
421
+ });
422
+ }
423
+ }
424
+
425
+ const link = new HTTPLink ({
426
+ url: ' http://where.is:4000/graphql' ,
427
+ headers : async () => {
428
+ const session = await getSession ();
429
+ if (session ) {
430
+ return {
431
+ Authorization: ` Bearer ${session .token } ` ,
432
+ };
433
+ }
434
+ },
435
+ });
436
+ ```
437
+
438
+ </details >
439
+
440
+ <details id =" request-retries " >
441
+ <summary ><a href =" #request-retries " >🔗</a > Client usage with request retries</summary >
442
+
443
+ ``` ts
444
+ import { createClient , NetworkError } from ' graphql-http' ;
445
+
446
+ const client = createClient ({
447
+ url: ' http://unstable.service:4000/graphql' ,
448
+ shouldRetry : async (err : NetworkError , retries : number ) => {
449
+ if (retries > 3 ) {
450
+ // max 3 retries and then report service down
451
+ return false ;
452
+ }
453
+
454
+ // try again when service unavailable, could be temporary
455
+ if (err .response ?.status === 503 ) {
456
+ // wait one second (you can alternatively time the promise resolution to your preference)
457
+ await new Promise ((resolve ) => setTimeout (resolve , 1000 ));
458
+ return true ;
459
+ }
460
+
461
+ // otherwise report error immediately
462
+ return false ;
463
+ },
464
+ });
465
+ ```
466
+
467
+ </details >
468
+
210
469
<details id =" auth " >
211
470
<summary ><a href =" #auth " >🔗</a > Server handler usage with authentication</summary >
212
471
0 commit comments