Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 5.9 kB view raw
1import { createStore, reconcile } from 'solid-js/store'; 2import { 3 type AnyVariables, 4 type DocumentInput, 5 type OperationContext, 6 type Operation, 7 type OperationResult, 8 type CombinedError, 9 createRequest, 10} from '@urql/core'; 11import { useClient } from './context'; 12import { pipe, onPush, filter, take, toPromise } from 'wonka'; 13import { batch } from 'solid-js'; 14 15export type CreateMutationState< 16 Data = any, 17 Variables extends AnyVariables = AnyVariables, 18> = { 19 /** Indicates whether `createMutation` is currently executing a mutation. */ 20 fetching: boolean; 21 22 /** Indicates that the mutation result is not fresh. 23 * 24 * @remarks 25 * The `stale` flag is set to `true` when a new result for the mutation 26 * is expected. 27 * This is mostly unused for mutations and will rarely affect you, and 28 * is more relevant for queries. 29 * 30 * @see {@link OperationResult.stale} for the source of this value. 31 */ 32 stale: boolean; 33 /** The {@link OperationResult.data} for the executed mutation. */ 34 data?: Data; 35 /** The {@link OperationResult.error} for the executed mutation. */ 36 error?: CombinedError; 37 /** The {@link OperationResult.extensions} for the executed mutation. */ 38 extensions?: Record<string, any>; 39 /** The {@link Operation} that the current state is for. 40 * 41 * @remarks 42 * This is the mutation {@link Operation} that has last been executed. 43 * When {@link CreateMutationState.fetching} is `true`, this is the 44 * last `Operation` that the current state was for. 45 */ 46 operation?: Operation<Data, Variables>; 47 /** The {@link OperationResult.hasNext} for the executed query. */ 48 hasNext: boolean; 49}; 50 51/** Triggers {@link createMutation} to execute its GraphQL mutation operation. 52 * 53 * @param variables - variables using which the mutation will be executed. 54 * @param context - optionally, context options that will be merged with the hook's 55 * context options and the `Client`’s options. 56 * @returns the {@link OperationResult} of the mutation. 57 * 58 * @remarks 59 * When called, {@link createMutation} will start the GraphQL mutation 60 * it currently holds and use the `variables` passed to it. 61 * 62 * Once the mutation response comes back from the API, its 63 * returned promise will resolve to the mutation’s {@link OperationResult} 64 * and the {@link CreateMutationState} will be updated with the result. 65 * 66 * @example 67 * ```ts 68 * const [result, executeMutation] = createMutation(UpdateTodo); 69 * const start = async ({ id, title }) => { 70 * const result = await executeMutation({ id, title }); 71 * }; 72 */ 73export type CreateMutationExecute< 74 Data = any, 75 Variables extends AnyVariables = AnyVariables, 76> = ( 77 variables: Variables, 78 context?: Partial<OperationContext> 79) => Promise<OperationResult<Data, Variables>>; 80 81/** Result tuple returned by the {@link createMutation} hook. 82 * 83 * @remarks 84 * Similarly to a `createSignal` hook’s return value, 85 * the first element is the {@link createMutation}’s state, updated 86 * as mutations are executed with the second value, which is 87 * used to start mutations and is a {@link CreateMutationExecute} 88 * function. 89 */ 90export type CreateMutationResult< 91 Data = any, 92 Variables extends AnyVariables = AnyVariables, 93> = [ 94 CreateMutationState<Data, Variables>, 95 CreateMutationExecute<Data, Variables>, 96]; 97 98/** Hook to create a GraphQL mutation, run by passing variables to the returned execute function. 99 * 100 * @param query - a GraphQL mutation document which `createMutation` will execute. 101 * @returns a {@link CreateMutationResult} tuple of a {@link CreateMutationState} result, 102 * and an execute function to start the mutation. 103 * 104 * @remarks 105 * `createMutation` allows GraphQL mutations to be defined and keeps its state 106 * after the mutation is started with the returned execute function. 107 * 108 * Given a GraphQL mutation document it returns state to keep track of the 109 * mutation state and a {@link CreateMutationExecute} function, which accepts 110 * variables for the mutation to be executed. 111 * Once called, the mutation executes and the state will be updated with 112 * the mutation’s result. 113 * 114 * @example 115 * ```ts 116 * import { gql, createMutation } from '@urql/solid'; 117 * 118 * const UpdateTodo = gql` 119 * mutation ($id: ID!, $title: String!) { 120 * updateTodo(id: $id, title: $title) { 121 * id, title 122 * } 123 * } 124 * `; 125 * 126 * const UpdateTodo = () => { 127 * const [result, executeMutation] = createMutation(UpdateTodo); 128 * const start = async ({ id, title }) => { 129 * const result = await executeMutation({ id, title }); 130 * }; 131 * // ... 132 * }; 133 * ``` 134 */ 135export const createMutation = < 136 Data = any, 137 Variables extends AnyVariables = AnyVariables, 138>( 139 query: DocumentInput<Data, Variables> 140): CreateMutationResult<Data, Variables> => { 141 const client = useClient(); 142 const initialResult: CreateMutationState<Data, Variables> = { 143 operation: undefined, 144 fetching: false, 145 hasNext: false, 146 stale: false, 147 data: undefined, 148 error: undefined, 149 extensions: undefined, 150 }; 151 152 const [state, setState] = 153 createStore<CreateMutationState<Data, Variables>>(initialResult); 154 155 const execute = ( 156 variables: Variables, 157 context?: Partial<OperationContext> 158 ) => { 159 setState({ ...initialResult, fetching: true }); 160 161 const request = createRequest(query, variables); 162 return pipe( 163 client.executeMutation(request, context), 164 onPush(result => { 165 batch(() => { 166 setState('data', reconcile(result.data)); 167 setState({ 168 fetching: false, 169 stale: result.stale, 170 error: result.error, 171 extensions: result.extensions, 172 operation: result.operation, 173 hasNext: result.hasNext, 174 }); 175 }); 176 }), 177 filter(result => !result.hasNext), 178 take(1), 179 toPromise 180 ); 181 }; 182 183 return [state, execute]; 184};