diff --git a/docs/interfaces/client.clientoptions.md b/docs/interfaces/client.clientoptions.md index 21d3e95c..f1ef3c8e 100644 --- a/docs/interfaces/client.clientoptions.md +++ b/docs/interfaces/client.clientoptions.md @@ -213,10 +213,18 @@ ___ ### url -• **url**: *string* +• **url**: *string* \| () => *string* \| *Promise* URL of the GraphQL over WebSocket Protocol compliant server to connect. +If the option is a function, it will be called on every WebSocket connection attempt. +Returning a promise is supported too and the connecting phase will stall until it +resolves with the URL. + +A good use-case for having a function is when using the URL for authentication, +where subsequent reconnects (due to auth) may have a refreshed identity token in +the URL. + ___ ### webSocketImpl diff --git a/src/client.ts b/src/client.ts index 863ee3da..7e2584ed 100644 --- a/src/client.ts +++ b/src/client.ts @@ -81,8 +81,18 @@ export type EventListener = E extends EventConnecting /** Configuration used for the GraphQL over WebSocket client. */ export interface ClientOptions { - /** URL of the GraphQL over WebSocket Protocol compliant server to connect. */ - url: string; + /** + * URL of the GraphQL over WebSocket Protocol compliant server to connect. + * + * If the option is a function, it will be called on every WebSocket connection attempt. + * Returning a promise is supported too and the connecting phase will stall until it + * resolves with the URL. + * + * A good use-case for having a function is when using the URL for authentication, + * where subsequent reconnects (due to auth) may have a refreshed identity token in + * the URL. + */ + url: string | (() => Promise | string); /** * Optional parameters, passed through the `payload` field with the `ConnectionInit` message, * that the client specifies when establishing a connection with the server. You can use this @@ -330,7 +340,10 @@ export function createClient(options: ClientOptions): Client { } emitter.emit('connecting'); - const socket = new WebSocketImpl(url, GRAPHQL_TRANSPORT_WS_PROTOCOL); + const socket = new WebSocketImpl( + typeof url === 'function' ? await url() : url, + GRAPHQL_TRANSPORT_WS_PROTOCOL, + ); socket.onerror = (err) => { // we let the onclose reject the promise for correct retry handling diff --git a/src/tests/client.ts b/src/tests/client.ts index 407eccef..061634eb 100644 --- a/src/tests/client.ts +++ b/src/tests/client.ts @@ -140,6 +140,19 @@ it('should use the provided WebSocket implementation', async () => { await server.waitForClient(); }); +it('should accept a function for the url', async () => { + const { url, ...server } = await startTServer(); + + createClient({ + url: () => Promise.resolve(url), + retryAttempts: 0, + onNonLazyError: noop, + lazy: false, + }); + + await server.waitForClient(); +}); + it('should not accept invalid WebSocket implementations', async () => { const { url } = await startTServer();