| name | absinthe-subscriptions |
| description | Use when implementing real-time GraphQL subscriptions with Absinthe. Covers Phoenix channels, PubSub, and subscription patterns. |
Absinthe - Subscriptions
Guide to implementing real-time GraphQL subscriptions with Absinthe and Phoenix.
Key Concepts
Basic Setup
# In your Phoenix endpoint
defmodule MyAppWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :my_app
use Absinthe.Phoenix.Endpoint
socket "/socket", MyAppWeb.UserSocket,
websocket: true,
longpoll: false
end
# Socket configuration
defmodule MyAppWeb.UserSocket do
use Phoenix.Socket
use Absinthe.Phoenix.Socket, schema: MyApp.Schema
def connect(params, socket, _connect_info) do
current_user = get_user_from_token(params["token"])
socket = Absinthe.Phoenix.Socket.put_options(socket,
context: %{current_user: current_user}
)
{:ok, socket}
end
def id(socket), do: "user_socket:#{socket.assigns.user_id}"
end
Defining Subscriptions
defmodule MyApp.Schema.Subscriptions do
use Absinthe.Schema.Notation
object :post_subscriptions do
field :post_created, :post do
config fn _args, _resolution ->
{:ok, topic: "posts"}
end
trigger :create_post, topic: fn _post ->
"posts"
end
end
field :post_updated, :post do
arg :id, non_null(:id)
config fn %{id: id}, _resolution ->
{:ok, topic: "post:#{id}"}
end
trigger :update_post, topic: fn post ->
"post:#{post.id}"
end
end
end
end
Publishing from Mutations
defmodule MyApp.Resolvers.Post do
def create_post(_parent, %{input: input}, _resolution) do
case MyApp.Posts.create_post(input) do
{:ok, post} ->
# Publish to subscription
Absinthe.Subscription.publish(
MyAppWeb.Endpoint,
post,
post_created: "posts"
)
{:ok, post}
{:error, changeset} ->
{:error, changeset}
end
end
end
User-Specific Subscriptions
field :user_notification, :notification do
config fn _args, %{context: %{current_user: user}} ->
{:ok, topic: "user:#{user.id}:notifications"}
end
end
# Publishing
Absinthe.Subscription.publish(
MyAppWeb.Endpoint,
notification,
user_notification: "user:#{user_id}:notifications"
)
Best Practices
- Scope subscriptions - Use topics to limit data exposure
- Authenticate connections - Verify users in socket connect
- Use triggers - Automatically publish on mutations
- Handle disconnections - Clean up resources on disconnect
- Rate limit subscriptions - Prevent abuse
PubSub Configuration
# config/config.exs
config :my_app, MyAppWeb.Endpoint,
pubsub_server: MyApp.PubSub
# application.ex
children = [
{Phoenix.PubSub, name: MyApp.PubSub},
MyAppWeb.Endpoint,
{Absinthe.Subscription, MyAppWeb.Endpoint}
]
Authorization in Subscriptions
field :private_messages, :message do
config fn _args, %{context: context} ->
case context do
%{current_user: %{id: user_id}} ->
{:ok, topic: "user:#{user_id}:messages"}
_ ->
{:error, "Unauthorized"}
end
end
end
Anti-Patterns
- Don't publish sensitive data to broad topics
- Avoid subscriptions without authentication
- Don't skip connection-level authorization
- Avoid overly granular topics (performance impact)