View Source Using Subscriptions
You can do this with Absinthe directly, and use AshGraphql.Subscription.query_for_subscription/3
. Here is an example of how you could do this for a subscription for a single record. This example could be extended to support lists of records as well.
# in your absinthe schema file
subscription do
field :field, :type_name do
config(fn
_args, %{context: %{current_user: %{id: user_id}}} ->
{:ok, topic: user_id, context_id: "user/#{user_id}"}
_args, _context ->
{:error, :unauthorized}
end)
resolve(fn args, _, resolution ->
# loads all the data you need
AshGraphql.Subscription.query_for_subscription(
YourResource,
YourDomain,
resolution
)
|> Ash.Query.filter(id == ^args.id)
|> Ash.read(actor: resolution.context.current_user)
end)
end
end
Subscription DSL (beta)
The subscription DSL is currently in beta and before using it you have to enable them in your config.
Subscription response order
The order in which the subscription responses are sent to the client is not guaranteed to be the same as the order in which the mutations were executed.
config :ash_graphql, :policies, show_policy_breakdowns?: true
First you'll need to do some setup, follow the the setup guide
in the absinthe docs, but instead of using Absinthe.Pheonix.Endpoint
use AshGraphql.Subscription.Endpoint
.
By default subscriptions are resolved synchronously as part of the mutation. This means that a resolver is run for every subscriber that
is not deduplicated. If you have a lot of subscribers you can add the AshGraphql.Subscription.Batcher
to your supervision tree, which
batches up notifications and runs subscription resolution out-of-band.
@impl true
def start(_type, _args) do
children = [
...,
{Absinthe.Subscription, MyAppWeb.Endpoint},
AshGraphql.Subscription.Batcher
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: MyAppWeb.Supervisor]
Supervisor.start_link(children, opts)
end
Afterwards, add an empty subscription block to your schema module.
defmodule MyAppWeb.Schema do
...
subscription do
end
end
Now you can define subscriptions on your resource or domain
defmodule MyApp.Resource do
use Ash.Resource,
data_layer: Ash.DataLayer.Ets,
extensions: [AshGraphql.Resource]
graphql do
subscriptions do
subscribe :resource_created do
action_types :create
end
end
end
end
For further Details checkout the DSL docs for resource and domain
Deduplication
By default, Absinthe will deduplicate subscriptions based on the context_id
.
We use the some of the context like actor and tenant to create a context_id
for you.
If you want to customize the deduplication you can do so by adding a actor function to your subscription. This function will be called with the actor that subscribes and you can return a more generic actor, this way you can have one actor for multiple users, which will lead to less resolver executions.
defmodule MyApp.Resource do
use Ash.Resource,
data_layer: Ash.DataLayer.Ets,
extensions: [AshGraphql.Resource]
graphql do
subscriptions do
subscribe :resource_created do
action_types :create
actor fn actor ->
if check_actor(actor) do
%{id: "your generic actor", ...}
else
actor
end
end
end
end
end
end