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```