Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 3.7 kB view raw
1import { type App, getCurrentScope, type Ref } from 'vue'; 2import { inject, provide, isRef, shallowRef } from 'vue'; 3import type { ClientOptions } from '@urql/core'; 4import { Client } from '@urql/core'; 5 6// WeakMap to store client instances as fallback when client is provided and used in the same component 7const clientsPerScope = new WeakMap<{}, Ref<Client>>(); 8 9/** Provides a {@link Client} to a component and it’s children. 10 * 11 * @param opts - {@link ClientOptions}, a {@link Client}, or a reactive ref object of a `Client`. 12 * 13 * @remarks 14 * `provideClient` provides a {@link Client} to `@urql/vue`’s GraphQL 15 * functions in children components. 16 * 17 * Hint: GraphQL functions and {@link useClient} will see the 18 * provided `Client`, even if `provideClient` has been called 19 * in the same component’s `setup` function. 20 * 21 * @example 22 * ```ts 23 * <script setup> 24 * import { provideClient } from '@urql/vue'; 25 * // All of `@urql/core` is also re-exported by `@urql/vue`: 26 * import { Client, cacheExchange, fetchExchange } from '@urql/core'; 27 * 28 * provideClient(new Client({ 29 * url: 'https://API', 30 * exchanges: [cacheExchange, fetchExchange], 31 * })); 32 * </script> 33 * ``` 34 */ 35export function provideClient(opts: ClientOptions | Client | Ref<Client>) { 36 let client: Ref<Client>; 37 if (!isRef(opts)) { 38 client = shallowRef(opts instanceof Client ? opts : new Client(opts)); 39 } else { 40 client = opts; 41 } 42 43 const scope = getCurrentScope(); 44 if (scope) { 45 clientsPerScope.set(scope, client); 46 } 47 48 provide('$urql', client); 49 return client.value; 50} 51 52/** Provides a {@link Client} to a Vue app. 53 * 54 * @param app - the Vue {@link App} 55 * @param opts - {@link ClientOptions}, a {@link Client}, or a reactive ref object of a `Client`. 56 * 57 * @remarks 58 * `install` provides a {@link Client} to `@urql/vue`’s GraphQL 59 * functions in a Vue app. 60 * 61 * @example 62 * ```ts 63 * import * as urql from '@urql/vue'; 64 * // All of `@urql/core` is also re-exported by `@urql/vue`: 65 * import { cacheExchange, fetchExchange } from '@urql/core'; 66 * 67 * import { createApp } from 'vue'; 68 * import Root from './App.vue'; 69 * 70 * const app = createApp(Root); 71 * app.use(urql, { 72 * url: 'http://localhost:3000/graphql', 73 * exchanges: [cacheExchange, fetchExchange], 74 * }); 75 * ``` 76 */ 77export function install(app: App, opts: ClientOptions | Client | Ref<Client>) { 78 let client: Ref<Client>; 79 if (!isRef(opts)) { 80 client = shallowRef(opts instanceof Client ? opts : new Client(opts)); 81 } else { 82 client = opts; 83 } 84 app.provide('$urql', client); 85} 86 87/** Returns a provided reactive ref object of a {@link Client}. 88 * 89 * @remarks 90 * `useClient` may be called in a reactive context to retrieve a 91 * reactive ref object of a {@link Client} that’s previously been 92 * provided with {@link provideClient} in the current or a parent’s 93 * `setup` function. 94 * 95 * @throws 96 * In development, if `useClient` is called outside of a reactive context 97 * or no {@link Client} was provided, an error will be thrown. 98 */ 99export function useClient(): Ref<Client> { 100 const scope = getCurrentScope(); 101 if (process.env.NODE_ENV !== 'production' && !scope) { 102 throw new Error( 103 'use* function must be called within a reactive context (component setup, composable, or effect scope).' 104 ); 105 } 106 107 let client = inject('$urql') as Ref<Client> | undefined; 108 if (!client) { 109 client = clientsPerScope.get(scope!); 110 } 111 112 if (process.env.NODE_ENV !== 'production' && !client) { 113 throw new Error( 114 'No urql Client was provided. Did you forget to install the plugin or call `provideClient` in a parent?' 115 ); 116 } 117 118 return client!; 119}