Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 450 lines 14 kB view raw view rendered
1--- 2title: Svelte Bindings 3order: 2 4--- 5 6# Svelte 7 8## Getting started 9 10This "Getting Started" guide covers how to install and set up `urql` and provide a `Client` for 11Svelte. The `@urql/svelte` package, which provides bindings for Svelte, doesn't fundamentally 12function differently from `@urql/preact` or `urql` and uses the same [Core Package and 13`Client`](./core.md). 14 15### Installation 16 17Installing `@urql/svelte` is quick and no other packages are immediately necessary. 18 19```sh 20yarn add @urql/svelte 21# or 22npm install --save @urql/svelte 23``` 24 25Most libraries related to GraphQL also need the `graphql` package to be installed as a peer 26dependency, so that they can adapt to your specific versioning requirements. That's why we'll need 27to install `graphql` alongside `@urql/svelte`. 28 29Both the `@urql/svelte` and `graphql` packages follow [semantic versioning](https://semver.org) and 30all `@urql/svelte` packages will define a range of compatible versions of `graphql`. Watch out 31for breaking changes in the future however, in which case your package manager may warn you about 32`graphql` being out of the defined peer dependency range. 33 34Note: if using Vite as your bundler, you might stumble upon the error `Function called outside component initialization`, which will prevent the page from loading. To fix it, you must add `@urql/svelte` to Vite's configuration property [`optimizeDeps.exclude`](https://vitejs.dev/config/#dep-optimization-options): 35 36```js 37{ 38 optimizeDeps: { 39 exclude: ['@urql/svelte'], 40 } 41 // other properties 42} 43``` 44 45### Setting up the `Client` 46 47The `@urql/svelte` package exports a `Client` class, which we can use to create 48the GraphQL client. This central `Client` manages all of our GraphQL requests and results. 49 50```js 51import { Client, cacheExchange, fetchExchange } from '@urql/svelte'; 52 53const client = new Client({ 54 url: 'http://localhost:3000/graphql', 55 exchanges: [cacheExchange, fetchExchange], 56}); 57``` 58 59At the bare minimum we'll need to pass an API's `url` and `exchanges` 60when we create a `Client` to get started. 61 62Another common option is `fetchOptions`. This option allows us to customize the options that will be 63passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object or 64a function returning an options object. 65 66In the following example we'll add a token to each `fetch` request that our `Client` sends to our 67GraphQL API. 68 69```js 70const client = new Client({ 71 url: 'http://localhost:3000/graphql', 72 exchanges: [cacheExchange, fetchExchange], 73 fetchOptions: () => { 74 const token = getToken(); 75 return { 76 headers: { authorization: token ? `Bearer ${token}` : '' }, 77 }; 78 }, 79}); 80``` 81 82### Providing the `Client` 83 84To make use of the `Client` in Svelte we will have to provide it via the 85[Context API](https://svelte.dev/tutorial/context-api). From a parent component to its child 86components. This will share one `Client` with the rest of our app, if we for instance provide the 87`Client` 88 89```html 90<script> 91 import { Client, setContextClient, cacheExchange, fetchExchange } from '@urql/svelte'; 92 93 const client = new Client({ 94 url: 'http://localhost:3000/graphql', 95 exchanges: [cacheExchange, fetchExchange], 96 }); 97 98 setContextClient(client); 99</script> 100``` 101 102The `setContextClient` method internally calls [Svelte's `setContext` 103function](https://svelte.dev/docs#run-time-svelte-setcontext). The `@urql/svelte` package also exposes a `getContextClient` 104function that uses [`getContext`](https://svelte.dev/docs#run-time-svelte-getcontext) to retrieve the `Client` in 105child components. This is used to input the client into `@urql/svelte`'s API. 106 107## Queries 108 109We'll implement queries using the `queryStore` function from `@urql/svelte`. 110 111The `queryStore` function creates a [Svelte Writable store](https://svelte.dev/docs#writable). 112You can use it to initialise a data container in `urql`. This store holds on to our query inputs, 113like the GraphQL query and variables, which we can change to launch new queries. It also exposes 114the query's eventual result, which we can then observe. 115 116### Run a first query 117 118For the following examples, we'll imagine that we're querying data from a GraphQL API that contains 119todo items. Let's dive right into it! 120 121```js 122<script> 123 import { queryStore, gql, getContextClient } from '@urql/svelte'; 124 125 const todos = queryStore({ 126 client: getContextClient(), 127 query: gql` 128 query { 129 todos { 130 id 131 title 132 } 133 } 134 `, 135 }); 136</script> 137 138{#if $todos.fetching} 139<p>Loading...</p> 140{:else if $todos.error} 141<p>Oh no... {$todos.error.message}</p> 142{:else} 143<ul> 144 {#each $todos.data.todos as todo} 145 <li>{todo.title}</li> 146 {/each} 147</ul> 148{/if} 149``` 150 151Here we have implemented our first GraphQL query to fetch todos. We're first creating a 152`queryStore` which will start our GraphQL query. 153 154The `todos` store can now be used like any other Svelte store using a 155[reactive auto-subscription](https://svelte.dev/tutorial/auto-subscriptions) in Svelte. This means 156that we prefix `$todos` with a dollar symbol, which automatically subscribes us to its changes. 157 158### Variables 159 160Typically we'll also need to pass variables to our queries, for instance, if we are dealing with 161pagination. For this purpose the `queryStore` also accepts a `variables` argument, which we can 162use to supply variables to our query. 163 164```js 165<script> 166 import { queryStore, getContextClient, gql } from '@urql/svelte'; 167 168 $: todos = queryStore({ 169 client: getContextClient(), 170 query: gql` 171 query ($from: Int!, $limit: Int!) { 172 todos(from: $from, limit: $limit) { 173 id 174 title 175 } 176 } 177 `, 178 variables: { from, limit } 179 }); 180</script> 181... 182``` 183 184> Note that we prefix the variable with `$` so Svelte knows that this store is reactive 185 186As when we're sending GraphQL queries manually using `fetch`, the variables will be attached to the 187`POST` request body that is sent to our GraphQL API. 188 189The `queryStore` also supports being actively changed. This will hook into Svelte's reactivity 190model as well and cause the `query` utility to start a new operation. 191 192```js 193<script> 194 import { queryStore, getContextClient, gql } from '@urql/svelte'; 195 196 let limit = 10; 197 let from = 0; 198 $: todos = queryStore({ 199 client: getContextClient(), 200 query: gql` 201 query ($from: Int!, $limit: Int!) { 202 todos(from: $from, limit: $limit) { 203 id 204 title 205 } 206 } 207 `, 208 variables: { from, limit } 209 ); 210 211 function nextPage() { 212 from = from + 10 213 } 214</script> 215 216<button on:click={nextPage}>Next page<button></button></button> 217``` 218 219### Pausing Queries 220 221In some cases we may want our queries to not execute until a pre-condition has been met. Since the 222`query` operation exists for the entire component lifecycle however, it can't just be stopped and 223started at will. Instead, the `queryStore` accepts a key named `pause` that will tell the store that 224is starts out as paused. 225 226For instance, we may start out with a paused store and then unpause it once a callback is invoked: 227 228```html 229<script> 230 import { queryStore, gql, getContextClient } from '@urql/svelte'; 231 232 $: todos = queryStore({ 233 client: getContextClient(), 234 query: gql` 235 query { 236 todos { 237 id 238 title 239 } 240 } 241 `, 242 pause: true, 243 }); 244 245 function unpause() { 246 todos.resume(); 247 } 248</script> 249 250<button on:click="{unpause}">Unpause</button> 251``` 252 253### Request Policies 254 255The `queryStore` also accepts another key apart from `query` and `variables`. Optionally 256you may pass a `requestPolicy`. 257 258The `requestPolicy` option determines how results are retrieved from our `Client`'s cache. By 259default, this is set to `cache-first`, which means that we prefer to get results from our cache, but 260are falling back to sending an API request. 261 262Request policies aren't specific to `urql`'s Svelte bindings, but are a common feature in its core. 263[You can learn more about how the cache behaves given the four different policies on the "Document 264Caching" page.](../basics/document-caching.md) 265 266```js 267<script> 268 import { queryStore, gql, getContextClient } from '@urql/svelte'; 269 270 $: todos = queryStore({ 271 client: getContextClient(), 272 query: gql` 273 query { 274 todos { 275 id 276 title 277 } 278 } 279 `, 280 requestPolicy: 'cache-and-network' 281 }); 282</script> 283 284... 285``` 286 287As we can see, the `requestPolicy` is easily changed by passing it directly as a "context option" 288when creating a `queryStore`. 289 290Internally, the `requestPolicy` is just one of several "**context** options". The `context` 291provides metadata apart from the usual `query` and `variables` we may pass. This means that 292we may also change the `Client`'s default `requestPolicy` by passing it there. 293 294```js 295import { Client, cacheExchange, fetchExchange } from '@urql/svelte'; 296 297const client = new Client({ 298 url: 'http://localhost:3000/graphql', 299 exchanges: [cacheExchange, fetchExchange], 300 // every operation will by default use cache-and-network rather 301 // than cache-first now: 302 requestPolicy: 'cache-and-network', 303}); 304``` 305 306### Context Options 307 308As mentioned, the `requestPolicy` option that we're passing to the `queryStore` is a part of 309`urql`'s context options. In fact, there are several more built-in context options, and the 310`requestPolicy` option is one of them. Another option we've already seen is the `url` option, which 311determines our API's URL. 312 313```js 314<script> 315 import { queryStore, gql, getContextClient } from '@urql/svelte'; 316 317 $: todos = queryStore({ 318 client: getContextClient(), 319 query: gql` 320 query { 321 todos { 322 id 323 title 324 } 325 } 326 `, 327 context: { url: 'http://localhost:3000/graphql?debug=true', } 328 }); 329</script> 330 331... 332``` 333 334As we can see, the `context` argument for `queryStore` accepts any known `context` option and 335can be used to alter them per query rather than globally. The `Client` accepts a subset of `context` 336options, while the `queryStore` argument does the same for a single query. They're then merged 337for your operation and form a full `Context` object for each operation, which means that any given 338query is able to override them as needed. 339 340[You can find a list of all `Context` options in the API docs.](../api/core.md#operationcontext) 341 342### Reexecuting queries 343 344Sometimes we'll need to arbitrarly reexecute a query to check for new data on the server, this can be done through: 345 346```jsx 347<script> 348 import { queryStore, gql, getContextClient } from '@urql/svelte'; 349 350 const client = getContextClient(); 351 const query = gql` 352 query { 353 todos { 354 id 355 title 356 } 357 } 358 ` 359 $: todos = queryStore({ 360 client, 361 query, 362 }); 363 364 function refresh() { 365 todos.reexecute({ 366 requestPolicy: 'network-only' 367 }); 368 } 369</script> 370``` 371 372We use the `requestPolicy` with value `network-only` so we don't hit our cache and dispatch a refresh, 373if it updates the data the `todos` will be updated due to our cache updating. 374 375### Reading on 376 377There are some more tricks we can use with `queryStore`. 378[Read more about its API in the API docs for it.](../api/svelte.md#queryStore) 379 380## Mutations 381 382The `mutationStore` function is similar to the `queryStore` function but is triggered manually and 383can accept a [`GraphQLRequest` object](../api/core.md#graphqlrequest). 384 385### Sending a mutation 386 387Let's again pick up an example with an imaginary GraphQL API for todo items, and dive into an 388example! We'll set up a mutation that _updates_ a todo item's title. 389 390```html 391<script> 392 import { mutationStore, gql, getContextClient } from '@urql/svelte'; 393 394 export let id; 395 396 let result; 397 let client = getContextClient(); 398 const updateTodo = ({ id, title }) => { 399 result = mutationStore({ 400 client, 401 query: gql` 402 mutation ($id: ID!, $title: String!) { 403 updateTodo(id: $id, title: $title) { 404 id 405 title 406 } 407 } 408 `, 409 variables: { id, title }, 410 }); 411 }; 412</script> 413``` 414 415This small call to `mutationStore` accepts a `query` property (besides the `variables` property) and 416returns the `OperationResult` as a store. 417 418Unlike the `query` function, we don't want the mutation to start automatically hence we enclose it in 419a function. The `result` will be updated with the `fetching`, `data`, ... as a normal query would which 420you can in-turn use in your UI. 421 422### Handling mutation errors 423 424It's worth noting that the promise we receive when calling the execute function will never 425reject. Instead it will always return a promise that resolves to an `mutationStore`, even if the 426mutation has failed. 427 428If you're checking for errors, you should use `mutationStore.error` instead, which will be set 429to a `CombinedError` when any kind of errors occurred while executing your mutation. 430[Read more about errors on our "Errors" page.](./errors.md) 431 432```jsx 433mutateTodo({ id, title: newTitle }).then(result => { 434 if (result.error) { 435 console.error('Oh no!', result.error); 436 } 437}); 438``` 439 440## Reading on 441 442This concludes the introduction for using `urql` with Svelte. The rest of the documentation 443is mostly framework-agnostic and will apply to either `urql` in general, or the `@urql/core` package, 444which is the same between all framework bindings. Hence, next we may want to learn more about one of 445the following to learn more about the internals: 446 447- [How does the default "document cache" work?](./document-caching.md) 448- [How are errors handled and represented?](./errors.md) 449- [A quick overview of `urql`'s architecture and structure.](../architecture.md) 450- [Setting up other features, like authentication, uploads, or persisted queries.](../advanced/README.md)