URQL: No exchange has handled operations of kind “subscription”

This error happens when you try to use subscriptions in your URQL app but don’t have a subscription exchange. Fix this by installing subscriptions-transport-ws and adding the subscriptionExchange to your URQL config.

This error was discovered when writing the real-time chat app example. If you haven’t already, you should check it out.

Let’s look at the cause of the problem.

Problem: you didn’t add a subscription exchange in your URQL config

Let’s say you’re using subscriptions, and this is your URQL client config:

import { createClient, defaultExchanges } from "urql";

export const urqlClient = createClient({
    url: `http://${process.env.REACT_APP_HOSTNAME}:8000/graphql`,
    exchanges: [...defaultExchanges]
});
JavaScript

The defaultExchanges list doesn’t contain the required subscriptionExchange to handle subscriptions. If you try to run the app like this, you’ll get the following error on your frontend:

No exchange has handled operations of kind "subscription". Check whether you've added an exchange responsible for these operations.

Solution: install the subscription transport and add the subscriptionExchange to your URQL config

To fix this, we first have to install the subscriptions-transport-ws package using NPM:

npm install subscriptions-transport-ws
ShellScript

Once that’s installed, we have to modify our URQL config:

import { createClient, defaultExchanges, subscriptionExchange } from "urql";
import { SubscriptionClient } from "subscriptions-transport-ws";

const subscriptionClient = new SubscriptionClient(
            `ws://${process.env.REACT_APP_HOSTNAME}:8000/graphql`, {
            reconnect: true,
            lazy: true
        });

export const urqlClient = createClient({
    url: `http://${process.env.REACT_APP_HOSTNAME}:8000/graphql`,
    exchanges: [
        subscriptionExchange({
            forwardSubscription: (operation) => subscriptionClient.request(operation)
        }),
        ...defaultExchanges
    ]
});
JavaScript

What we did here was:

  • Create and configure a SubscriptionClient
  • Add the subscriptionExchange to our list of exchanges

Create and configure a SubscriptionClient

The first thing we have to do is import, create, and configure the SubscriptionClient:

import { SubscriptionClient } from "subscriptions-transport-ws";

const subscriptionClient = new SubscriptionClient(
            `ws://${process.env.REACT_APP_HOSTNAME}:8000/graphql`, {
            reconnect: true,
            lazy: true
        });

export const urqlClient = createClient({
JavaScript

The SubscriptionClient class can be imported from the subscriptions-transport-ws package. Once imported, we have to instantiate and configure it. Since subscriptions use WebSockets, we have to again specify the GraphQL endpoint for our app, except instead of http or https, we put ws or wss as the transport protocol. Additional configuration options can also be passed as an object for the second argument. Here, we set reconnect: true and lazy: true. These control how the connection is established and maintained.

Add the subscription exchange

Next, we add our subscriptionExchange:

export const urqlClient = createClient({
    url: `http://${process.env.REACT_APP_HOSTNAME}:8000/graphql`,
    exchanges: [
        subscriptionExchange({
            forwardSubscription: (operation) => subscriptionClient.request(operation)
        }),
        ...defaultExchanges
    ]
});
JavaScript

The subscriptionExchange can be imported from the urql package. To add it to our list of exchanges for this URQL client, we pass in an object with the forwardSubscription field set to an arrow function that takes the operation as a parameter, and then passes that to the request method of the subscriptionClient we configured in the previous step.

Finally, we use the “splat” operator (...) to put the rest of the defaultExchanges in our list.

Conclusion

We covered what causes the error “No exchange has handled operations of kind subscription” and how to fix it. In short, it’s caused by not adding the subscriptionExchange to our list of exchanges when we configured our URQL client. To do this, we also have to install subscriptions-transport-ws and configure subscriptionExchange to use it.