Urql graphql subscriptions with Absinthe

Many Graphql stacks use Apollo as the javascript client library. It's well maintained, popular and has lot's of examples. It also works together with Absinthe, the Elixir Graphql library. If you need to use graphql subscriptions in combination with Apollo and Absinthe there is the socket-apollo-link to connect the two.

There's another interesting javascript client library for Graphql on the horizon, Urql. It's a bit more lightweight and looks really promising. However, it's not as straightforward to hookup Urql subscriptions with Absinthe. I'll show how to do this.

In the Urql documentation it is shown how to connect subscription to the Apollo server backend

import { Client, defaultExchanges, subscriptionExchange } from "urql";
import { SubscriptionClient } from "subscriptions-transport-ws";
const subscriptionClient = new SubscriptionClient("wss://localhost/graphql", {
  reconnect: true,
});
const client = new Client({
  url: "/graphql",
  exchanges: [
    ...defaultExchanges,
    subscriptionExchange({
      forwardSubscription(operation) {
        return subscriptionClient.request(operation);
      },
    }),
  ],
});

For Absinthe we cannot rely on the subscriptions-transport-ws package due to how subscriptions in Absinthe work in combination with Phoenix channels.

We can use the @absinthe/socket package however. The forwardSubscription() function should return an Observable object so we'll have to do that. So, to hookup Urql to Absinthe subscriptions you can do the following.

import { Client, defaultExchanges, subscriptionExchange } from "urql";
import * as withAbsintheSocket from "@absinthe/socket";
import { Socket as PhoenixSocket } from "phoenix";

const phoenixSocket = new PhoenixSocket("ws://localhost:4000/socket");
const absintheSocket = withAbsintheSocket.create(phoenixSocket);

const client = new Client({
  url: "http://localhost:4000/api",
  exchanges: [
    ...defaultExchanges,
    subscriptionExchange({
      forwardSubscription: (operation) => {
        const notifier = withAbsintheSocket.send(absintheSocket, {
          operation: operation.query,
          variables: operation.variables,
        });

        return withAbsintheSocket.toObservable(absintheSocket, notifier, {});
      },
    }),
  ],
});

So, we send the subscription operation with the variables over the socket, and return an Observable from the notifier. This is all that is needed to hookup Urql to Absinthe. It can be extracted into a package but that is not really needed.