AbsintheClient. WebSocket
(AbsintheClient v0.2.0)
View Source
Req adapter for Absinthe subscriptions.
The WebSocket does the following:
Pushes documents to the GraphQL server and forwards replies to the callers.
Manages any subscriptions received, including automatically re-subscribing in the event of a connection loss.
Under the hood, WebSocket connections are Slipstream
socket processes which are usually managed by an internal
AbsintheClient supervisor.
Examples
Performing a query operation over a WebSocket:
iex> req = Req.new(base_url: "http://localhost:4002") |> AbsintheClient.attach()
iex> ws = req |> AbsintheClient.WebSocket.connect!()
iex> Req.request!(req, web_socket: ws, graphql: ~S|{ __type(name: "Repo") { name } }|).body["data"]
%{"__type" => %{"name" => "Repo"}}Performing an async query operation and awaiting the reply:
iex> req = Req.new(base_url: "http://localhost:4002") |> AbsintheClient.attach()
iex> ws = req |> AbsintheClient.WebSocket.connect!()
iex> reply =
...> req
...> |> Req.request!(web_socket: ws, async: true, graphql: ~S|{ __type(name: "Repo") { name } }|)
...> |> AbsintheClient.WebSocket.await_reply!()
iex> reply.payload["data"]
%{"__type" => %{"name" => "Repo"}}Handling messages
Results will be sent to the caller as
WebSocket.Message structs.
In a GenServer for instance, you would implement a
handle_info/2 callback:
def handle_info(%AbsintheClient.WebSocket.Message{payload: payload}, state) do
# code...
{:noreply, state}
end
Summary
Functions
Awaits the server's response to a pushed document.
Awaits the server's response to a pushed document or raises an error.
Dynamically starts (or re-uses already started) AbsintheWs process with the given options
Connects to an Absinthe WebSocket.
Same as connect/1 but raises on error.
Same as connect/2 but raises on error.
Pushes a query to the server via the given socket.
Performs a GraphQL operation.
Types
@type web_socket() :: GenServer.server()
Functions
@spec await_reply(Req.Response.t() | reference(), non_neg_integer()) :: {:ok, AbsintheClient.WebSocket.Reply.t()} | {:error, :timeout}
Awaits the server's response to a pushed document.
Examples
iex> req = Req.new(base_url: "http://localhost:4002") |> AbsintheClient.attach(async: true)
iex> {:ok, ws} = AbsintheClient.WebSocket.connect(req)
iex> {:ok, res} = Req.request(req, web_socket: ws, graphql: ~S|{ __type(name: "Repo") { name } }|)
iex> {:ok, reply} = AbsintheClient.WebSocket.await_reply(res)
iex> reply.payload["data"]
%{"__type" => %{"name" => "Repo"}}
@spec await_reply!(Req.Response.t() | reference(), non_neg_integer()) :: AbsintheClient.WebSocket.Reply.t()
Awaits the server's response to a pushed document or raises an error.
Examples
iex> req = Req.new(base_url: "http://localhost:4002") |> AbsintheClient.attach(async: true)
iex> ws = req |> AbsintheClient.WebSocket.connect!()
iex> res = Req.post!(req, web_socket: ws, graphql: ~S|{ __type(name: "Repo") { name } }|)
iex> AbsintheClient.WebSocket.await_reply!(res).payload["data"]
%{"__type" => %{"name" => "Repo"}}
@spec connect(request_or_options :: Req.Request.t() | keyword()) :: {:ok, web_socket()} | {:error, Exception.t()}
Dynamically starts (or re-uses already started) AbsintheWs process with the given options:
:url- URL where to make the WebSocket connection. When provided as an option toconnect/2the request'sbase_urlwill be prepended to this path. The default value is"/socket/websocket".:headers- headers to send on the initial HTTP request. Defaults to[].:connect_options- list of options given toMint.HTTP.connect/4for the initial HTTP request::timeout- socket connect timeout in milliseconds, defaults to30_000.:transport_opts- keyword list of options passed to the underlying transport layer (SSL/TCP). Common options include::verify-:verify_peeror:verify_nonefor SSL verification:cacertfile- path to CA certificate file:certfile- path to client certificate for mTLS:keyfile- path to client private key for mTLS:versions- list of allowed TLS versions (e.g.,[:"tlsv1.2", :"tlsv1.3"]):nodelay- boolean to disable Nagle's algorithm for lower latency:timeout- connection timeout (defaults to30_000if not specified)
See the Erlang :ssl module documentation for a complete list of available options.
:connect_params- Optional. Custom params to be sent when the WebSocket connects. Defaults to sending the bearer Authorization token if one is present on the request. The default value isnil.:parent- pid of the process starting the connection. The socket monitors this process and shuts down when the parent process exits. Defaults toself().
Note that when connect/2 returns successfully, it indicates that
the WebSocket process has started. The process must then connect
to the GraphQL server and join the relevant topic(s) before it can
send and receive messages.
Examples
From a request:
iex> req = Req.new(base_url: "http://localhost:4002") |> AbsintheClient.attach()
iex> {:ok, ws} = req |> AbsintheClient.WebSocket.connect()
iex> ws |> GenServer.whereis() |> Process.alive?()
trueFrom keyword options:
iex> {:ok, ws} = AbsintheClient.WebSocket.connect(url: "ws://localhost:4002/socket/websocket")
iex> ws |> GenServer.whereis() |> Process.alive?()
trueDisabling SSL verification for local development:
iex> req = Req.new(base_url: "https://localhost:4002") |> AbsintheClient.attach()
iex> {:ok, ws} = req |> AbsintheClient.WebSocket.connect(
...> connect_options: [transport_opts: [verify: :verify_none]]
...> )
iex> ws |> GenServer.whereis() |> Process.alive?()
trueUsing client certificates for mTLS:
req = Req.new(base_url: "https://example.com") |> AbsintheClient.attach()
{:ok, ws} = req |> AbsintheClient.WebSocket.connect(
connect_options: [
transport_opts: [
verify: :verify_peer,
cacertfile: "/path/to/ca.pem",
certfile: "/path/to/client-cert.pem",
keyfile: "/path/to/client-key.pem"
]
]
)
ws |> GenServer.whereis() |> Process.alive?()
# ==> true
@spec connect(Req.Request.t(), keyword()) :: {:ok, web_socket()} | {:error, Exception.t()}
Connects to an Absinthe WebSocket.
Refer to connect/1 for more information.
Examples
With the default URL path:
iex> req = Req.new(base_url: "http://localhost:4002") |> AbsintheClient.attach()
iex> {:ok, ws} = req |> AbsintheClient.WebSocket.connect()
iex> ws |> GenServer.whereis() |> Process.alive?()
trueWith a custom URL path:
iex> req = Req.new(base_url: "http://localhost:4002") |> AbsintheClient.attach()
iex> {:ok, ws} = req |> AbsintheClient.WebSocket.connect(url: "/socket/websocket")
iex> ws |> GenServer.whereis() |> Process.alive?()
true
@spec connect!(request_or_options :: Req.Request.t() | keyword()) :: web_socket()
Same as connect/1 but raises on error.
Examples
From a request:
iex> ws = Req.new(base_url: "http://localhost:4002") |> AbsintheClient.WebSocket.connect!()
iex> ws |> GenServer.whereis() |> Process.alive?()
trueFrom keyword options:
iex> ws = AbsintheClient.WebSocket.connect!(url: "ws://localhost:4002/socket/websocket")
iex> ws |> GenServer.whereis() |> Process.alive?()
true
@spec connect!(Req.Request.t(), keyword()) :: web_socket()
Same as connect/2 but raises on error.
Examples
iex> ws =
...> Req.new(base_url: "http://localhost:4002")
...> |> AbsintheClient.WebSocket.connect!(url: "/socket/websocket")
iex> ws |> GenServer.whereis() |> Process.alive?()
true
@spec push(request_or_socket :: Req.Request.t() | web_socket(), graphql()) :: reference()
Pushes a query to the server via the given socket.
Examples
iex> {:ok, req} = AbsintheClient.WebSocket.connect(url: "ws://localhost:4002/socket/websocket")
iex> ref = AbsintheClient.WebSocket.push(req, ~S|{ __type(name: "Repo") { name } }|)
iex> AbsintheClient.WebSocket.await_reply!(ref).payload["data"]
%{"__type" => %{"name" => "Repo"}}
@spec run(Req.Request.t()) :: {Req.Request.t(), Req.Response.t() | Exception.t()}
Performs a GraphQL operation.
Examples
iex> req = Req.new(base_url: "http://localhost:4002") |> AbsintheClient.attach()
iex> ws = req |> AbsintheClient.WebSocket.connect!()
iex> Req.request!(req, web_socket: ws, graphql: ~S|{ __type(name: "Repo") { name } }|).body["data"]
%{"__type" => %{"name" => "Repo"}}