Implementing Absinthe Subscriptions without Phoenix?

Hey!

I’ve been trying to write an Elixir GraphQL API without the use of Phoenix. I’ve been using Plug, Cowboy, and Absinthe as my stack.

I wanted to look into implementing subscriptions over websocket but could not find a documentation to do it. I can find the Understanding Subscriptions page in the docs, that says:

Like queries and mutations, subscriptions are not intrinsically tied to any particular transport, and they’re built within Absinthe itself to be able to operate on many different platforms.

But the documentation is only focused on Phoenix which is fine. However the documentation for Absinthe.Subscription.child_spec explicitly calls for a Phoenix.Pubsub.

Questions:

  • Is it really possible to use subscriptions without Phoenix?
  • Is there any documentation / example code that I could look at?
  • If there is no complete example code, could someone give me a few links that would guide me towards an implementation?

Thanks in advance! :slight_smile:

Has anyone done something like this?
Meanwhile I tried skimming through Crafting GraphQL APIs in Elixir with GraphQL, where it was mentioned that Subscriptions are not tied to Phoenix, and that the book explores them outside of the framework, but haven’t been able to find where that happens :slight_smile:
If anyone knows where that happens that would be helpful as well!

Hey @blascsi. It’s true that the docs reference Phoenix a fair bit, as that’s generally what everybody is using. However if you checkout the Absinthe.Subscription.Pubsub — absinthe v1.7.1 behavior docs you’ll see that you could provide your own module that implements these callbacks, and the docs cover roughly what is expected of each callback.

Do you have an existing multi-node pubsub you want to use, or are you going to roll your own?

1 Like

Thanks for the reply! I found this behavior and started messing around with it but haven’t got far yet :slight_smile:

Do you have an existing multi-node pubsub you want to use, or are you going to roll your own?

I haven’t got one, I just found the pubsub package on hex, and wondered if it would be a good fit to use within this behaviour.

@blascsi, just in case you hadn’t noticed: Phoenix.PubSub, despite the name, is its own package that has no dependency on any part of the rest of Phoenix. phoenix_pubsub | Hex

1 Like

:open_mouth: Thank you so much for calling that out, I haven’t noticed that!
I’ll take a look at that package!

1 Like

I’ve been working on this over the weekend and hit a new roadblock.

I created a very simple websocket handler like this:

defmodule MyApp.SocketHandler do
  @behaviour :cowboy_websocket

  def init(req, state) do
    {:cowboy_websocket, req, state}
  end

  def websocket_init(state) do
    {:ok, state}
  end

  def websocket_handle(req, state) do
    {:reply, req, state}
  end

  def websocket_info(info, state) do
    {:reply, info, state}
  end

  def terminate(_reason, _req, _state) do
    :ok
  end
end

It does not do anything just upgrades the connections to WS, and echoes back the messages it gets. So far so good. I was trying to do this because I wanted to see the messages that Absinthe sends to the WS, and set up my Pubsub subscription that way. However all I’m seeing when I start the subscription from Graphiql is phx_join, phx_leave and phoenix pings over the socket.

Anyone knows how am I supposed to receive the query the client tries to execute? It’s not in the query parameters either.

If you’re doing Websockets as well as pubsub, you’re gonna be reinventing the Phoenix wheel a fair bit. I would seriously reconsider the choice to stick with just Plug. The Absinthe Phoenix JS libraries all assume you have Phoenix on hand.