Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 6.4 kB view raw
1import type { 2 Client, 3 GraphQLRequestParams, 4 AnyVariables, 5 OperationContext, 6 RequestPolicy, 7} from '@urql/core'; 8import { createRequest } from '@urql/core'; 9 10import type { Source } from 'wonka'; 11import { 12 pipe, 13 map, 14 fromValue, 15 switchMap, 16 subscribe, 17 concat, 18 scan, 19 never, 20} from 'wonka'; 21 22import { derived, writable } from 'svelte/store'; 23 24import type { 25 OperationResultState, 26 OperationResultStore, 27 Pausable, 28} from './common'; 29import { initialResult, createPausable, fromStore } from './common'; 30 31/** Input arguments for the {@link queryStore} function. 32 * 33 * @param query - The GraphQL query that the `queryStore` executes. 34 * @param variables - The variables for the GraphQL query that `queryStore` executes. 35 */ 36export type QueryArgs< 37 Data = any, 38 Variables extends AnyVariables = AnyVariables, 39> = { 40 /** The {@link Client} using which the query will be executed. 41 * 42 * @remarks 43 * If you’ve previously provided a {@link Client} on Svelte’s context 44 * this can be set to {@link getContextClient}’s return value. 45 */ 46 client: Client; 47 /** Updates the {@link OperationContext} for the executed GraphQL query operation. 48 * 49 * @remarks 50 * `context` may be passed to {@link queryStore}, to update the {@link OperationContext} 51 * of a query operation. This may be used to update the `context` that exchanges 52 * will receive for a single hook. 53 * 54 * @example 55 * ```ts 56 * queryStore({ 57 * query, 58 * context: { 59 * additionalTypenames: ['Item'], 60 * }, 61 * }); 62 * ``` 63 */ 64 context?: Partial<OperationContext>; 65 /** Sets the {@link RequestPolicy} for the executed GraphQL query operation. 66 * 67 * @remarks 68 * `requestPolicy` modifies the {@link RequestPolicy} of the GraphQL query operation 69 * that the {@link queryStore} executes, and indicates a caching strategy for cache exchanges. 70 * 71 * For example, when set to `'cache-and-network'`, the `queryStore` will 72 * receive a cached result with `stale: true` and an API request will be 73 * sent in the background. 74 * 75 * @see {@link OperationContext.requestPolicy} for where this value is set. 76 */ 77 requestPolicy?: RequestPolicy; 78 /** Prevents the {@link queryStore} from automatically executing GraphQL query operations. 79 * 80 * @remarks 81 * `pause` may be set to `true` to stop the {@link queryStore} from executing 82 * automatically. The store will stop receiving updates from the {@link Client} 83 * and won’t execute the query operation, until either it’s set to `false` 84 * or {@link Pausable.resume} is called. 85 * 86 * @see {@link https://urql.dev/goto/docs/basics/svelte#pausing-queries} for 87 * documentation on the `pause` option. 88 */ 89 pause?: boolean; 90} & GraphQLRequestParams<Data, Variables>; 91 92/** Function to create a `queryStore` that runs a GraphQL query and updates with GraphQL results. 93 * 94 * @param args - a {@link QueryArgs} object, to pass a `query`, `variables`, and options. 95 * @returns a {@link OperationResultStore} of query results, which implements {@link Pausable}. 96 * 97 * @remarks 98 * `queryStore` allows GraphQL queries to be defined as Svelte stores. 99 * Given {@link QueryArgs.query}, it executes the GraphQL query on the 100 * {@link QueryArgs.client}. 101 * 102 * The returned store updates with {@link OperationResult} values when 103 * the `Client` has new results for the query. 104 * 105 * @see {@link https://urql.dev/goto/docs/basics/svelte#queries} for `queryStore` docs. 106 * 107 * @example 108 * ```ts 109 * import { queryStore, gql, getContextClient } from '@urql/svelte'; 110 * 111 * const todos = queryStore({ 112 * client: getContextClient(), 113 * query: gql`{ todos { id, title } }`, 114 * }); 115 * ``` 116 */ 117export function queryStore< 118 Data = any, 119 Variables extends AnyVariables = AnyVariables, 120>( 121 args: QueryArgs<Data, Variables> 122): OperationResultStore<Data, Variables> & 123 Pausable & { reexecute: (context: Partial<OperationContext>) => void } { 124 const request = createRequest(args.query, args.variables as Variables); 125 126 const context: Partial<OperationContext> = { 127 requestPolicy: args.requestPolicy, 128 ...args.context, 129 }; 130 131 const operation = args.client.createRequestOperation( 132 'query', 133 request, 134 context 135 ); 136 137 const operation$ = writable(operation); 138 139 const initialState: OperationResultState<Data, Variables> = { 140 ...initialResult, 141 operation, 142 }; 143 144 const isPaused$ = writable(!!args.pause); 145 146 const result$ = writable(initialState, () => { 147 return pipe( 148 fromStore(isPaused$), 149 switchMap( 150 (isPaused): Source<Partial<OperationResultState<Data, Variables>>> => { 151 if (isPaused) { 152 return never as any; 153 } 154 155 return pipe( 156 fromStore(operation$), 157 switchMap(operation => { 158 return concat<Partial<OperationResultState<Data, Variables>>>([ 159 fromValue({ fetching: true, stale: false, hasNext: false }), 160 pipe( 161 args.client.executeRequestOperation(operation), 162 map( 163 ({ 164 stale, 165 data, 166 error, 167 extensions, 168 operation, 169 hasNext, 170 }) => ({ 171 fetching: false, 172 stale: !!stale, 173 data, 174 error, 175 operation, 176 extensions, 177 hasNext, 178 }) 179 ) 180 ), 181 fromValue({ fetching: false, hasNext: false }), 182 ]); 183 }) 184 ); 185 } 186 ), 187 scan( 188 (result: OperationResultState<Data, Variables>, partial) => ({ 189 ...result, 190 ...partial, 191 }), 192 initialState 193 ), 194 subscribe(result => { 195 result$.set(result); 196 }) 197 ).unsubscribe; 198 }); 199 200 const reexecute = (context: Partial<OperationContext>) => { 201 const newContext = { ...context, ...args.context }; 202 const operation = args.client.createRequestOperation( 203 'query', 204 request, 205 newContext 206 ); 207 isPaused$.set(false); 208 operation$.set(operation); 209 }; 210 211 return { 212 ...derived(result$, (result, set) => { 213 set(result); 214 }), 215 ...createPausable(isPaused$), 216 reexecute, 217 }; 218}