Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1--- 2title: Subscriptions 3order: 0 4--- 5 6# Subscriptions 7 8One feature of `urql` that was not mentioned in the ["Basics" sections](../basics/README.md) is `urql`'s 9APIs and ability to handle GraphQL subscriptions. 10 11## The Subscription Exchange 12 13To add support for subscriptions we need to add the `subscriptionExchange` to our `Client`. 14 15```js 16import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql'; 17 18const client = new Client({ 19 url: 'http://localhost:3000/graphql', 20 exchanges: [ 21 cacheExchange, 22 fetchExchange, 23 subscriptionExchange({ 24 forwardSubscription, 25 }), 26 ], 27}); 28``` 29 30Read more about Exchanges and how they work [on the "Authoring Exchanges" 31page.](./authoring-exchanges.md) or what they are [on the "Architecture" 32page.](../architecture.md) 33 34In the above example, we add the `subscriptionExchange` to the `Client` with the default exchanges 35added before it. The `subscriptionExchange` is a factory that accepts additional options and returns 36the actual `Exchange` function. It does not make any assumption over the transport protocol and 37scheme that is used. Instead, we need to pass a `forwardSubscription` function. 38 39The `forwardSubscription` is called when the `subscriptionExchange` receives an `Operation`, so 40typically, when you’re executing a GraphQL subscription. This will call the `forwardSubscription` 41function with a GraphQL request body, in the same shape that a GraphQL HTTP API may receive it as 42JSON input. 43 44If you’re using TypeScript, you may notice that the input that `forwardSubscription` receives has 45an optional `query` property. This is because of persisted query support. For some transports, the 46`query` property may have to be defaulted to an empty string, which matches the GraphQL over HTTP 47specification more closely. 48 49When we define this function it must return an "Observable-like" object, which needs to follow the 50[Observable spec](https://github.com/tc39/proposal-observable), which comes down to having an 51object with a `.subscribe()` method accepting an observer. 52 53### Setting up `graphql-ws` 54 55For backends supporting `graphql-ws`, we recommend using the [graphql-ws](https://github.com/enisdenjo/graphql-ws) client. 56 57```js 58import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql'; 59import { createClient as createWSClient } from 'graphql-ws'; 60 61const wsClient = createWSClient({ 62 url: 'ws://localhost/graphql', 63}); 64 65const client = new Client({ 66 url: '/graphql', 67 exchanges: [ 68 cacheExchange, 69 fetchExchange, 70 subscriptionExchange({ 71 forwardSubscription(request) { 72 const input = { ...request, query: request.query || '' }; 73 return { 74 subscribe(sink) { 75 const unsubscribe = wsClient.subscribe(input, sink); 76 return { unsubscribe }; 77 }, 78 }; 79 }, 80 }), 81 ], 82}); 83``` 84 85In this example, we're creating a `SubscriptionClient`, are passing in a URL and some parameters, 86and are using the `SubscriptionClient`'s `request` method to create a Subscription Observable, which 87we return to the `subscriptionExchange` inside `forwardSubscription`. 88 89[Read more on the `graphql-ws` README.](https://github.com/enisdenjo/graphql-ws/blob/master/README.md) 90 91### Setting up `subscriptions-transport-ws` 92 93For backends supporting `subscriptions-transport-ws`, [Apollo's `subscriptions-transport-ws` 94package](https://github.com/apollographql/subscriptions-transport-ws) can be used. 95 96> The `subscriptions-transport-ws` package isn't actively maintained. If your API supports the new protocol or you can swap the package out, consider using [`graphql-ws`](#setting-up-graphql-ws) instead. 97 98```js 99import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql'; 100import { SubscriptionClient } from 'subscriptions-transport-ws'; 101 102const subscriptionClient = new SubscriptionClient('ws://localhost/graphql', { reconnect: true }); 103 104const client = new Client({ 105 url: '/graphql', 106 exchanges: [ 107 cacheExchange, 108 fetchExchange, 109 subscriptionExchange({ 110 forwardSubscription: request => subscriptionClient.request(request), 111 }), 112 ], 113}); 114``` 115 116In this example, we're creating a `SubscriptionClient`, are passing in a URL and some parameters, 117and are using the `SubscriptionClient`'s `request` method to create a Subscription Observable, which 118we return to the `subscriptionExchange` inside `forwardSubscription`. 119 120[Read more about `subscription-transport-ws` on its README.](https://github.com/apollographql/subscriptions-transport-ws/blob/master/README.md) 121 122### Using `fetch` for subscriptions 123 124Some GraphQL backends (for example GraphQL Yoga) support built-in transport protocols that 125can execute subscriptions via a simple HTTP fetch call. 126In fact, this is how `@defer` and `@stream` directives are supported. These transports can 127also be used for subscriptions. 128 129```js 130import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql'; 131 132const client = new Client({ 133 url: '/graphql', 134 fetchSubscriptions: true, 135 exchanges: [cacheExchange, fetchExchange], 136}); 137``` 138 139In this example, we only need to enable `fetchSubscriptions: true` on the `Client`, and the 140`fetchExchange` will be used to send subscriptions to the API. If your API supports this transport, 141it will stream results back to the `fetchExchange`. 142 143[You can find a code example of subscriptions via `fetch` in an example in the `urql` repository.](https://github.com/urql-graphql/urql/tree/main/examples/with-subscriptions-via-fetch) 144 145## React & Preact 146 147The `useSubscription` hooks comes with a similar API to `useQuery`, which [we've learned about in 148the "Queries" page in the "Basics" section.](../basics/react-preact.md#queries) 149 150Its usage is extremely similar in that it accepts options, which may contain `query` and 151`variables`. However, it also accepts a second argument, which is a reducer function, similar to 152what you would pass to `Array.prototype.reduce`. 153 154It receives the previous set of data that this function has returned or `undefined`. 155As the second argument, it receives the event that has come in from the subscription. 156You can use this to accumulate the data over time, which is useful for a 157list for example. 158 159In the following example, we create a subscription that informs us of 160new messages. We will concatenate the incoming messages so that we 161can display all messages that have come in over the subscription across 162events. 163 164```js 165import React from 'react'; 166import { useSubscription } from 'urql'; 167 168const newMessages = ` 169 subscription MessageSub { 170 newMessages { 171 id 172 from 173 text 174 } 175 } 176`; 177 178const handleSubscription = (messages = [], response) => { 179 return [response.newMessages, ...messages]; 180}; 181 182const Messages = () => { 183 const [res] = useSubscription({ query: newMessages }, handleSubscription); 184 185 if (!res.data) { 186 return <p>No new messages</p>; 187 } 188 189 return ( 190 <ul> 191 {res.data.map(message => ( 192 <p key={message.id}> 193 {message.from}: "{message.text}" 194 </p> 195 ))} 196 </ul> 197 ); 198}; 199``` 200 201As we can see, the `res.data` is being updated and transformed by 202the `handleSubscription` function. This works over time, so as 203new messages come in, we will append them to the list of previous 204messages. 205 206[Read more about the `useSubscription` API in the API docs for it.](../api/urql.md#usesubscription) 207 208## Svelte 209 210The `subscriptionStore` function in `@urql/svelte` comes with a similar API to `query`, which [we've 211learned about in the "Queries" page in the "Basics" section.](../basics/svelte.md#queries) 212 213Its usage is extremely similar in that it accepts an `operationStore`, which will typically contain 214our GraphQL subscription query. 215 216In the following example, we create a subscription that informs us of new messages. 217 218```js 219<script> 220 import { gql, getContextClient, subscriptionStore } from '@urql/svelte'; 221 222 const messages = subscriptionStore({ 223 client: getContextClient(), 224 query: gql` 225 subscription MessageSub { 226 newMessages { 227 id 228 from 229 text 230 } 231 } 232 `, 233 }); 234</script> 235 236{#if !$messages.data} 237 <p>No new messages</p> 238{:else} 239 <ul> 240 {#each $messages.data.newMessages as message} 241 <li>{message.from}: "{message.text}"</li> 242 {/each} 243 </ul> 244{/if} 245 246``` 247 248As we can see, `$messages.data` is being updated and transformed by the `$messages` subscriptionStore. This works over time, so as new messages come in, we will append them to 249the list of previous messages. 250 251`subscriptionStore` optionally accepts a second argument, a handler function, allowing custom update behavior from the subscription. 252 253[Read more about the `subscription` API in the API docs for it.](../api/svelte.md#subscriptionstore) 254 255## Vue 256 257The `useSubscription` API is very similar to `useQuery`, which [we've learned about in 258the "Queries" page in the "Basics" section.](../basics/vue.md#queries) 259 260Its usage is extremely similar in that it accepts options, which may contain `query` and 261`variables`. However, it also accepts a second argument, which is a reducer function, similar to 262what you would pass to `Array.prototype.reduce`. 263 264It receives the previous set of data that this function has returned or `undefined`. 265As the second argument, it receives the event that has come in from the subscription. 266You can use this to accumulate the data over time, which is useful for a 267list for example. 268 269In the following example, we create a subscription that informs us of 270new messages. We will concatenate the incoming messages so that we 271can display all messages that have come in over the subscription across 272events. 273 274```jsx 275<template> 276 <div v-if="error"> 277 Oh no... {{error}} 278 </div> 279 <div v-else> 280 <ul v-if="data"> 281 <li v-for="msg in data">{{ msg.from }}: "{{ msg.text }}"</li> 282 </ul> 283 </div> 284</template> 285 286<script> 287import { useSubscription } from '@urql/vue'; 288 289export default { 290 setup() { 291 const handleSubscription = (messages = [], response) => { 292 return [response.newMessages, ...messages]; 293 }; 294 295 const result = useSubscription({ 296 query: ` 297 subscription MessageSub { 298 newMessages { 299 id 300 from 301 text 302 } 303 } 304 `, 305 }, handleSubscription) 306 307 return { 308 data: result.data, 309 error: result.error, 310 }; 311 } 312}; 313</script> 314``` 315 316As we can see, the `result.data` is being updated and transformed by 317the `handleSubscription` function. This works over time, so as 318new messages come in, we will append them to the list of previous 319messages. 320 321[Read more about the `useSubscription` API in the API docs for it.](../api/vue.md#usesubscription) 322 323## One-off Subscriptions 324 325When you're using subscriptions directly without `urql`'s framework bindings, you can use the 326`Client`'s `subscription` method for one-off subscriptions. This method is similar to the ones for 327mutations and subscriptions [that we've seen before on the "Core Package" page.](../basics/core.md) 328 329This method will always [returns a Wonka stream](../architecture.md#the-wonka-library) and doesn't 330have a `.toPromise()` shortcut method, since promises won't return the multiple values that a 331subscription may deliver. Let's convert the above example to one without framework code, as we may 332use subscriptions in a Node.js environment. 333 334```js 335import { gql } from '@urql/core'; 336 337const MessageSub = gql` 338 subscription MessageSub { 339 newMessages { 340 id 341 from 342 text 343 } 344 } 345`; 346 347const { unsubscribe } = client.subscription(MessageSub).subscribe(result => { 348 console.log(result); // { data: ... } 349}); 350```