Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1---
2title: Solid Bindings
3order: 3
4---
5
6# Solid
7
8This guide covers how to install and setup `@urql/solid` and the `Client`, as well as query and mutate data with Solid. The `@urql/solid` package provides reactive primitives that integrate seamlessly with Solid's fine-grained reactivity system.
9
10> **Note:** This guide is for client-side SolidJS applications. If you're building a SolidStart application with SSR, see the [SolidStart guide](./solid-start.md) instead. The packages use different APIs optimized for their respective use cases.
11
12## Getting started
13
14### Installation
15
16Installing `@urql/solid` is quick and you won't need any other packages to get started with at first. We'll install the package with our package manager of choice.
17
18```sh
19yarn add @urql/solid graphql
20# or
21npm install --save @urql/solid graphql
22# or
23pnpm add @urql/solid graphql
24```
25
26Most libraries related to GraphQL also need the `graphql` package to be installed as a peer dependency, so that they can adapt to your specific versioning requirements. That's why we'll need to install `graphql` alongside `@urql/solid`.
27
28Both the `@urql/solid` and `graphql` packages follow [semantic versioning](https://semver.org) and all `@urql/solid` packages will define a range of compatible versions of `graphql`. Watch out for breaking changes in the future however, in which case your package manager may warn you about `graphql` being out of the defined peer dependency range.
29
30### Setting up the `Client`
31
32The `@urql/solid` package exports a `Client` class from `@urql/core`, which we can use to create the GraphQL client. This central `Client` manages all of our GraphQL requests and results.
33
34```js
35import { createClient, cacheExchange, fetchExchange } from '@urql/solid';
36
37const client = createClient({
38 url: 'http://localhost:3000/graphql',
39 exchanges: [cacheExchange, fetchExchange],
40});
41```
42
43At the bare minimum we'll need to pass an API's `url` and `exchanges` when we create a `Client` to get started.
44
45Another common option is `fetchOptions`. This option allows us to customize the options that will be passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object, or a function returning an options object.
46
47In the following example we'll add a token to each `fetch` request that our `Client` sends to our GraphQL API.
48
49```js
50const client = createClient({
51 url: 'http://localhost:3000/graphql',
52 exchanges: [cacheExchange, fetchExchange],
53 fetchOptions: () => {
54 const token = getToken();
55 return {
56 headers: { authorization: token ? `Bearer ${token}` : '' },
57 };
58 },
59});
60```
61
62### Providing the `Client`
63
64To make use of the `Client` in Solid we will have to provide it via Solid's Context API. This may be done with the help of the `Provider` export.
65
66```jsx
67import { render } from 'solid-js/web';
68import { createClient, Provider, cacheExchange, fetchExchange } from '@urql/solid';
69
70const client = createClient({
71 url: 'http://localhost:3000/graphql',
72 exchanges: [cacheExchange, fetchExchange],
73});
74
75const App = () => (
76 <Provider value={client}>
77 <YourRoutes />
78 </Provider>
79);
80
81render(() => <App />, document.getElementById('root'));
82```
83
84Now every component inside and under the `Provider` can use GraphQL queries that will be sent to our API.
85
86## Queries
87
88The `@urql/solid` package offers a `createQuery` primitive that integrates with Solid's fine-grained reactivity system.
89
90### Run a first query
91
92For the following examples, we'll imagine that we're querying data from a GraphQL API that contains todo items. Let's dive right into it!
93
94```jsx
95import { Suspense, For } from 'solid-js';
96import { gql } from '@urql/core';
97import { createQuery } from '@urql/solid';
98
99const TodosQuery = gql`
100 query {
101 todos {
102 id
103 title
104 }
105 }
106`;
107
108const Todos = () => {
109 const [result] = createQuery({
110 query: TodosQuery,
111 });
112
113 return (
114 <Suspense fallback={<p>Loading...</p>}>
115 <ul>
116 <For each={result().data.todos}>
117 {(todo) => <li>{todo.title}</li>}
118 </For>
119 </ul>
120 </Suspense>
121 );
122};
123```
124
125Here we have implemented our first GraphQL query to fetch todos. We see that `createQuery` accepts options and returns a tuple. In this case we've set the `query` option to our GraphQL query. The tuple we then get in return is an array where the first item is an accessor function that returns the result object.
126
127The result object contains several properties. The `fetching` field indicates whether the query is loading data, `data` contains the actual `data` from the API's result, and `error` is set when either the request to the API has failed or when our API result contained some `GraphQLError`s, which we'll get into later on the ["Errors" page](./errors.md).
128
129### Variables
130
131Typically we'll also need to pass variables to our queries, for instance, if we are dealing with pagination. For this purpose `createQuery` also accepts a `variables` option, which can be reactive.
132
133```jsx
134const TodosListQuery = gql`
135 query ($from: Int!, $limit: Int!) {
136 todos(from: $from, limit: $limit) {
137 id
138 title
139 }
140 }
141`;
142
143const Todos = (props) => {
144 const [result] = createQuery({
145 query: TodosListQuery,
146 variables: () => ({ from: props.from, limit: props.limit }),
147 });
148
149 // ...
150};
151```
152
153The `variables` option can be passed as a static object or as an accessor function that returns the variables. When using an accessor, the query will automatically re-execute when the variables change.
154
155```jsx
156import { Suspense, For, createSignal } from 'solid-js';
157import { gql } from '@urql/core';
158import { createQuery } from '@urql/solid';
159
160const TodosListQuery = gql`
161 query ($from: Int!, $limit: Int!) {
162 todos(from: $from, limit: $limit) {
163 id
164 title
165 }
166 }
167`;
168
169const Todos = () => {
170 const [from, setFrom] = createSignal(0);
171 const limit = 10;
172
173 const [result] = createQuery({
174 query: TodosListQuery,
175 variables: () => ({ from: from(), limit }),
176 });
177
178 return (
179 <div>
180 <Suspense fallback={<p>Loading...</p>}>
181 <ul>
182 <For each={result().data.todos}>
183 {(todo) => <li>{todo.title}</li>}
184 </For>
185 </ul>
186 </Suspense>
187 <button onClick={() => setFrom(f => f + 10)}>Next Page</button>
188 </div>
189 );
190};
191```
192
193Whenever the variables change, `fetching` will switch to `true`, and a new request will be sent to our API, unless a result has already been cached previously.
194
195### Pausing `createQuery`
196
197In some cases we may want `createQuery` to execute a query when a pre-condition has been met, and not execute the query otherwise. For instance, we may be building a form and want a validation query to only take place when a field has been filled out.
198
199The `createQuery` primitive accepts a `pause` option that temporarily stops the query from executing.
200
201```jsx
202const Todos = (props) => {
203 const shouldPause = () => props.from == null || props.limit == null;
204
205 const [result] = createQuery({
206 query: TodosListQuery,
207 variables: () => ({ from: props.from, limit: props.limit }),
208 pause: shouldPause,
209 });
210
211 // ...
212};
213```
214
215Now whenever the mandatory variables aren't supplied the query won't be executed. This also means that `result().data` won't change, which means we'll still have access to our old data even though the variables may have changed.
216
217### Request Policies
218
219The `createQuery` primitive accepts a `requestPolicy` option that determines how results are retrieved from our `Client`'s cache. By default, this is set to `cache-first`, which means that we prefer to get results from our cache, but are falling back to sending an API request.
220
221Request policies aren't specific to `@urql/solid`, but are a common feature in urql's core. [You can learn more about how the cache behaves given the four different policies on the "Document Caching" page.](./document-caching.md)
222
223```jsx
224const [result] = createQuery({
225 query: TodosListQuery,
226 variables: () => ({ from: props.from, limit: props.limit }),
227 requestPolicy: 'cache-and-network',
228});
229```
230
231The `requestPolicy` can be passed as a static string or as an accessor function. When using `cache-and-network`, the query will be refreshed from our API even after our cache has given us a cached result.
232
233### Context Options
234
235The `requestPolicy` option is part of urql's context options. In fact, there are several more built-in context options. These options can be passed via the `context` parameter.
236
237```jsx
238const [result] = createQuery({
239 query: TodosListQuery,
240 variables: () => ({ from: props.from, limit: props.limit }),
241 context: () => ({
242 requestPolicy: 'cache-and-network',
243 url: 'http://localhost:3000/graphql?debug=true',
244 }),
245});
246```
247
248[You can find a list of all `Context` options in the API docs.](../api/core.md#operationcontext)
249
250### Reexecuting Queries
251
252The `createQuery` primitive updates and executes queries automatically when reactive inputs change, but in some cases we may need to programmatically trigger a new query. This is the purpose of the second item in the tuple that `createQuery` returns.
253
254```jsx
255const Todos = () => {
256 const [result, reexecuteQuery] = createQuery({
257 query: TodosListQuery,
258 variables: { from: 0, limit: 10 },
259 });
260
261 const refresh = () => {
262 // Refetch the query and skip the cache
263 reexecuteQuery({ requestPolicy: 'network-only' });
264 };
265
266 return (
267 <div>
268 <Suspense fallback={<p>Loading...</p>}>
269 <ul>
270 <For each={result().data.todos}>
271 {(todo) => <li>{todo.title}</li>}
272 </For>
273 </ul>
274 </Suspense>
275 <button onClick={refresh}>Refresh</button>
276 </div>
277 );
278};
279```
280
281Calling `refresh` in the above example will execute the query again forcefully, and will skip the cache, since we're passing `requestPolicy: 'network-only'`.
282
283## Mutations
284
285The `@urql/solid` package offers a `createMutation` primitive for executing GraphQL mutations.
286
287### Sending a mutation
288
289Let's again pick up an example with an imaginary GraphQL API for todo items. We'll set up a mutation that updates a todo item's title.
290
291```jsx
292import { gql } from '@urql/core';
293import { createMutation } from '@urql/solid';
294
295const UpdateTodo = gql`
296 mutation ($id: ID!, $title: String!) {
297 updateTodo (id: $id, title: $title) {
298 id
299 title
300 }
301 }
302`;
303
304const Todo = (props) => {
305 const [result, updateTodo] = createMutation(UpdateTodo);
306
307 const handleSubmit = (newTitle) => {
308 updateTodo({ id: props.id, title: newTitle });
309 };
310
311 return (
312 <div>
313 <Show when={result().fetching}>
314 <p>Updating...</p>
315 </Show>
316 <Show when={result().error}>
317 <p>Error: {result().error.message}</p>
318 </Show>
319 {/* Your form UI here */}
320 </div>
321 );
322};
323```
324
325Similar to `createQuery`, `createMutation` returns a tuple. The first item is an accessor that returns the result object containing `fetching`, `error`, and `data` — identical to query results. The second item is the execute function that triggers the mutation.
326
327Unlike `createQuery`, `createMutation` doesn't execute automatically. We must call the execute function with the mutation variables.
328
329### Using the mutation result
330
331The mutation result is available both through the reactive accessor and through the promise returned by the execute function.
332
333```jsx
334const Todo = (props) => {
335 const [result, updateTodo] = createMutation(UpdateTodo);
336
337 const handleSubmit = (newTitle) => {
338 const variables = { id: props.id, title: newTitle };
339
340 updateTodo(variables).then((result) => {
341 // The result is almost identical to result() from the accessor
342 // It is an OperationResult.
343 if (!result.error) {
344 console.log('Todo updated!', result.data);
345 }
346 });
347 };
348
349 return (
350 <div>
351 <Show when={result().fetching}>
352 <p>Updating...</p>
353 </Show>
354 {/* Your form UI here */}
355 </div>
356 );
357};
358```
359
360The reactive accessor is useful when your UI needs to display progress on the mutation, and the returned promise is particularly useful for side effects that run after the mutation completes.
361
362### Handling mutation errors
363
364The promise returned by the execute function will never reject. Instead it will always return a promise that resolves to a result.
365
366If you're checking for errors, you should use `result.error`, which will be set to a `CombinedError` when any kind of errors occurred while executing your mutation. [Read more about errors on our "Errors" page.](./errors.md)
367
368```jsx
369const Todo = (props) => {
370 const [result, updateTodo] = createMutation(UpdateTodo);
371
372 const handleSubmit = (newTitle) => {
373 const variables = { id: props.id, title: newTitle };
374
375 updateTodo(variables).then((result) => {
376 if (result.error) {
377 console.error('Oh no!', result.error);
378 }
379 });
380 };
381
382 // ...
383};
384```
385
386## Subscriptions
387
388The `@urql/solid` package offers a `createSubscription` primitive for handling GraphQL subscriptions with Solid's reactive system.
389
390### Setting up a subscription
391
392GraphQL subscriptions allow you to receive real-time updates from your GraphQL API. Here's an example of how to set up a subscription:
393
394```jsx
395import { gql } from '@urql/core';
396import { createSubscription } from '@urql/solid';
397
398const NewTodos = gql`
399 subscription {
400 newTodos {
401 id
402 title
403 }
404 }
405`;
406
407const TodoSubscription = () => {
408 const [result] = createSubscription({
409 query: NewTodos,
410 });
411
412 return (
413 <div>
414 <Show when={result().fetching}>
415 <p>Waiting for updates...</p>
416 </Show>
417 <Show when={result().error}>
418 <p>Error: {result().error.message}</p>
419 </Show>
420 <Show when={result().data}>
421 <p>New todo: {result().data.newTodos.title}</p>
422 </Show>
423 </div>
424 );
425};
426```
427
428### Handling subscription data
429
430Unlike queries and mutations, subscriptions can emit multiple results over time. You can use a `handler` function to accumulate or process subscription events:
431
432```jsx
433import { createSignal } from 'solid-js';
434
435const TodoSubscription = () => {
436 const [todos, setTodos] = createSignal([]);
437
438 const handleSubscription = (previousData, newData) => {
439 setTodos(current => [...current, newData.newTodos]);
440 return newData;
441 };
442
443 const [result] = createSubscription(
444 {
445 query: NewTodos,
446 },
447 handleSubscription
448 );
449
450 return (
451 <ul>
452 <For each={todos()}>
453 {(todo) => <li>{todo.title}</li>}
454 </For>
455 </ul>
456 );
457};
458```
459
460The handler function receives the previous data and the new data from the subscription, allowing you to accumulate results or transform them as needed.
461
462## Reading on
463
464This concludes the introduction for using `@urql/solid` with Solid. The rest of the documentation is mostly framework-agnostic and will apply to either `urql` in general, or the `@urql/core` package, which is the same between all framework bindings. Hence, next we may want to learn more about one of the following:
465
466- [How does the default "document cache" work?](./document-caching.md)
467- [How are errors handled and represented?](./errors.md)
468- [A quick overview of `urql`'s architecture and structure.](../architecture.md)
469- [Setting up other features, like authentication, uploads, or persisted queries.](../advanced/README.md)