maartenvanvliet / subscriptions-transport-ws

Socket library implementation of the subscriptions-transport-ws graphql subscription protocol for Absinthe

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SubscriptionsTransportWS

Hex pm Hex Docs License.github/workflows/elixir.yml

Implementation of the subscriptions-transport-ws graphql subscription protocol for Absinthe. Instead of using Absinthe subscriptions over Phoenix channels it exposes a websocket directly. This allows you to use the Apollo and Urql Graphql clients without using a translation layer to Phoenix channels such as @absinthe/socket.

Has been tested with Apollo iOS/ Apollo JS and Urql with subscriptions-transport-ws.

Subscriptions-Transport-WS vs Graphql-WS

subscriptions-transport-ws is an older protocol. A newer one has been written named graphql_ws. The grapqhl_ws protocol is more robust, and the way to go in the future.

At the time of writing the major libraries support one, the other or both. E.g. Apollo Swift currently only supports subscriptions-transport-ws, v3 of Apollo Android supports graphql_ws. The Urlq/Apollo JS libraries support either one.

If you need to support graphql_ws on the backend in Elixir, you can use the absinthe_graphql_ws library. You can set up multiple websocket endpoints to support both protocols.

Installation

The package can be installed by adding subscriptions_transport_ws to your list of dependencies in mix.exs:

def deps do
  [
    {:subscriptions_transport_ws, "~> 1.0.0"}
  ]
end

Usage

There are several steps to use this library.

You need to have a working phoenix pubsub configured. Here is what the default looks like if you create a new phoenix project:

config :my_app, MyAppWeb.Endpoint,
  # ... other config
  pubsub_server: MyApp.PubSub

In your application supervisor add a line AFTER your existing endpoint supervision line:

[
  # other children ...
  MyAppWeb.Endpoint, # this line should already exist
  {Absinthe.Subscription, MyAppWeb.Endpoint}, # add this line
  # other children ...
]

Where MyAppWeb.Endpoint is the name of your application's phoenix endpoint.

Add a module in your app lib/web/channels/absinthe_socket.ex

defmodule AbsintheSocket do
  # App.GraphqlSchema is your graphql schema
  use SubscriptionsTransportWS.Socket, schema: App.GraphqlSchema, keep_alive: 1000

  # Callback similar to default Phoenix UserSocket
  @impl true
  def connect(params, socket) do
    {:ok, socket}
  end

  # Callback to authenticate the user
  @impl true
  def gql_connection_init(message, socket) do
    {:ok, socket}
  end
end

In your MyAppWeb.Endpoint module add:

  defmodule MyAppWeb.Endpoint do
    use Phoenix.Endpoint, otp_app: :my_app
    use Absinthe.Phoenix.Endpoint

    socket("/absinthe-ws", AbsintheSocket, websocket: [subprotocols: ["graphql-ws"]])
    # ...
  end

Now if you start your app you can connect to the socket on ws://localhost:4000/absinthe-ws/websocket

Example with Apollo JS

import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
  useSubscription,
} from "@apollo/client";
import { split, HttpLink } from "@apollo/client";
import { getMainDefinition } from "@apollo/client/utilities";
import { WebSocketLink } from "@apollo/client/link/ws";

const wsLink = new WebSocketLink({
  uri: "ws://localhost:4000/absinthe-ws/websocket",
  options: {
    reconnect: true,
  },
});
const httpLink = new HttpLink({
  uri: "http://localhost:4000/api",
});

const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === "OperationDefinition" &&
      definition.operation === "subscription"
    );
  },
  wsLink,
  httpLink
);

const client = new ApolloClient({
  uri: "http://localhost:4000/api",
  cache: new InMemoryCache(),
  link: splitLink,
});

See the Apollo documentation for more information

Example with Urql

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

const subscriptionClient = new SubscriptionClient(
  "ws://localhost:4000/absinthe-ws/websocket",
  {
    reconnect: true,
  }
);

const client = new Client({
  url: "http://localhost:4000/api",
  exchanges: [
    subscriptionExchange({
      forwardSubscription(operation) {
        return subscriptionClient.request(operation);
      },
    }),
    ...defaultExchanges,
  ],
});

See the Urql documentation for more information.

Example with Swift Apollo

import Apollo
import ApolloSQLite
import ApolloWebSocket
import Foundation
import Combine

class ApolloService {
    static let shared = ApolloService()
    static let url = Config.host.appendingPathComponent("api")
  
    private(set) lazy var client: ApolloClient = {

        let store = ApolloStore()
        
        let requestChainTransport = RequestChainNetworkTransport(
            interceptorProvider: DefaultInterceptorProvider(store: store),
            endpointURL: "https://localhost:4000/api"
        )
        
        // The Normal Apollo Web Socket Implementation which uses an Apollo adapter server side
        let wsUrl = "wss://localhost:4000/absinthe-ws/websocket"
        let wsRequest = URLRequest(url: wsUrl)
        let wsClient = WebSocket(request: wsRequest)
        let apolloWebSocketTransport =  WebSocketTransport(websocket: wsClient)

        let splitNetworkTransport = SplitNetworkTransport(
            uploadingNetworkTransport: requestChainTransport,
            webSocketNetworkTransport: apolloWebSocketTransport
          )

        // Remember to give the store you already created to the client so it
        // doesn't create one on its own
        let client =  ApolloClient(
            networkTransport: splitNetworkTransport,
            store: store
        )

        return client
    }()
}

Or see here https://www.apollographql.com/docs/ios/subscriptions/#subscriptions-and-authorization-tokens

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/subscriptions_transport_ws.

About

Socket library implementation of the subscriptions-transport-ws graphql subscription protocol for Absinthe

License:MIT License


Languages

Language:Elixir 100.0%