@@ -18,7 +18,11 @@ import {
18
18
logForFunction ,
19
19
Logger ,
20
20
} from "./logging.js" ;
21
- import { FunctionArgs , UserIdentityAttributes } from "../server/index.js" ;
21
+ import {
22
+ ArgsAndOptions ,
23
+ FunctionArgs ,
24
+ UserIdentityAttributes ,
25
+ } from "../server/index.js" ;
22
26
23
27
export const STATUS_CODE_OK = 200 ;
24
28
export const STATUS_CODE_BAD_REQUEST = 400 ;
@@ -33,15 +37,25 @@ export function setFetch(f: typeof globalThis.fetch) {
33
37
specifiedFetch = f ;
34
38
}
35
39
40
+ export type HttpMutationOptions = {
41
+ /**
42
+ * Skip the default queue of mutations and run this immediately.
43
+ *
44
+ * This allows the same HttpConvexClient to be used to request multiple
45
+ * mutations in parallel, something not possible with WebSocket-based clients.
46
+ */
47
+ skipQueue : boolean ;
48
+ } ;
49
+
36
50
/**
37
51
* A Convex client that runs queries and mutations over HTTP.
38
52
*
53
+ * This client is stateful (it has user credentials and queues mutations)
54
+ * so take care to avoid sharing it between requests in a server.
55
+ *
39
56
* This is appropriate for server-side code (like Netlify Lambdas) or non-reactive
40
57
* webapps.
41
58
*
42
- * If you're building a React app, consider using
43
- * {@link react.ConvexReactClient} instead.
44
- *
45
59
* @public
46
60
*/
47
61
export class ConvexHttpClient {
@@ -52,6 +66,14 @@ export class ConvexHttpClient {
52
66
private debug : boolean ;
53
67
private fetchOptions ?: FetchOptions ;
54
68
private logger : Logger ;
69
+ private mutationQueue : Array < {
70
+ mutation : FunctionReference < "mutation" > ;
71
+ args : FunctionArgs < any > ;
72
+ resolve : ( value : any ) => void ;
73
+ reject : ( error : any ) => void ;
74
+ } > = [ ] ;
75
+ private isProcessingQueue : boolean = false ;
76
+
55
77
/**
56
78
* Create a new {@link ConvexHttpClient}.
57
79
*
@@ -61,15 +83,19 @@ export class ConvexHttpClient {
61
83
* - `skipConvexDeploymentUrlCheck` - Skip validating that the Convex deployment URL looks like
62
84
* `https://happy-animal-123.convex.cloud` or localhost. This can be useful if running a self-hosted
63
85
* Convex backend that uses a different URL.
64
- * - `logger` - A logger. If not provided, logs to the console.
86
+ * - `logger` - A logger or a boolean . If not provided, logs to the console.
65
87
* You can construct your own logger to customize logging to log elsewhere
66
- * or not log at all.
88
+ * or not log at all, or use `false` as a shorthand for a no-op logger.
89
+ * - `auth` - A JWT containing identity claims accessible in Convex functions.
90
+ * This identity may expire so it may be necessary to call `setAuth()` later,
91
+ * but for short-lived clients it's convenient to specify this value here.
67
92
*/
68
93
constructor (
69
94
address : string ,
70
95
options ?: {
71
96
skipConvexDeploymentUrlCheck ?: boolean ;
72
97
logger ?: Logger | boolean ;
98
+ auth ?: string ;
73
99
} ,
74
100
) {
75
101
if ( typeof options === "boolean" ) {
@@ -124,6 +150,9 @@ export class ConvexHttpClient {
124
150
}
125
151
126
152
/**
153
+ * Set admin auth token to allow calling internal queries, mutations, and actions
154
+ * and acting as an identity.
155
+ *
127
156
* @internal
128
157
*/
129
158
setAdminAuth ( token : string , actingAsIdentity ?: UserIdentityAttributes ) {
@@ -299,19 +328,10 @@ export class ConvexHttpClient {
299
328
}
300
329
}
301
330
302
- /**
303
- * Execute a Convex mutation function.
304
- *
305
- * @param name - The name of the mutation.
306
- * @param args - The arguments object for the mutation. If this is omitted,
307
- * the arguments will be `{}`.
308
- * @returns A promise of the mutation's result.
309
- */
310
- async mutation < Mutation extends FunctionReference < "mutation" > > (
331
+ private async mutationInner < Mutation extends FunctionReference < "mutation" > > (
311
332
mutation : Mutation ,
312
- ... args : OptionalRestArgs < Mutation >
333
+ mutationArgs : FunctionArgs < Mutation > ,
313
334
) : Promise < FunctionReturnType < Mutation > > {
314
- const mutationArgs = parseArgs ( args [ 0 ] ) ;
315
335
const name = getFunctionName ( mutation ) ;
316
336
const body = JSON . stringify ( {
317
337
path : name ,
@@ -359,8 +379,60 @@ export class ConvexHttpClient {
359
379
}
360
380
}
361
381
382
+ private async processMutationQueue ( ) {
383
+ if ( this . isProcessingQueue ) {
384
+ return ;
385
+ }
386
+
387
+ this . isProcessingQueue = true ;
388
+ while ( this . mutationQueue . length > 0 ) {
389
+ const { mutation, args, resolve, reject } = this . mutationQueue . shift ( ) ! ;
390
+ try {
391
+ const result = await this . mutationInner ( mutation , args ) ;
392
+ resolve ( result ) ;
393
+ } catch ( error ) {
394
+ reject ( error ) ;
395
+ }
396
+ }
397
+ this . isProcessingQueue = false ;
398
+ }
399
+
400
+ private enqueueMutation < Mutation extends FunctionReference < "mutation" > > (
401
+ mutation : Mutation ,
402
+ args : FunctionArgs < Mutation > ,
403
+ ) : Promise < FunctionReturnType < Mutation > > {
404
+ return new Promise ( ( resolve , reject ) => {
405
+ this . mutationQueue . push ( { mutation, args, resolve, reject } ) ;
406
+ void this . processMutationQueue ( ) ;
407
+ } ) ;
408
+ }
409
+
410
+ /**
411
+ * Execute a Convex mutation function. Mutations are queued by default.
412
+ *
413
+ * @param name - The name of the mutation.
414
+ * @param args - The arguments object for the mutation. If this is omitted,
415
+ * the arguments will be `{}`.
416
+ * @param options - An optional object containing
417
+ * @returns A promise of the mutation's result.
418
+ */
419
+ async mutation < Mutation extends FunctionReference < "mutation" > > (
420
+ mutation : Mutation ,
421
+ ...args : ArgsAndOptions < Mutation , HttpMutationOptions >
422
+ ) : Promise < FunctionReturnType < Mutation > > {
423
+ const [ fnArgs , options ] = args ;
424
+ const mutationArgs = parseArgs ( fnArgs ) ;
425
+ const queued = ! options ?. skipQueue ;
426
+
427
+ if ( queued ) {
428
+ return await this . enqueueMutation ( mutation , mutationArgs ) ;
429
+ } else {
430
+ return await this . mutationInner ( mutation , mutationArgs ) ;
431
+ }
432
+ }
433
+
362
434
/**
363
- * Execute a Convex action function.
435
+ * Execute a Convex action function. Actions are not queued.
364
436
*
365
437
* @param name - The name of the action.
366
438
* @param args - The arguments object for the action. If this is omitted,
@@ -420,7 +492,7 @@ export class ConvexHttpClient {
420
492
}
421
493
422
494
/**
423
- * Execute a Convex function of an unknown type.
495
+ * Execute a Convex function of an unknown type. These function calls are not queued.
424
496
*
425
497
* @param name - The name of the function.
426
498
* @param args - The arguments object for the function. If this is omitted,
0 commit comments