WebsockexNova.Adapter (WebsockexNova v0.1.0)

View Source

Macro for building WebsockexNova adapters with minimal boilerplate.

This macro provides a convenient way to create platform-specific WebSocket adapters by automatically implementing all required behaviors with sensible defaults. You only need to override the specific behaviors needed for your use case.

Basic Usage

defmodule MyApp.MyAdapter do
  use WebsockexNova.Adapter

  # Override only what you need:
  @impl WebsockexNova.Behaviors.MessageHandler
  def handle_message(message, state) do
    # Custom message handling
    {:ok, decoded_message, state}
  end
end

What this macro does

  • Declares all core @behaviour attributes (AuthHandler, ConnectionHandler, etc.)
  • Injects default implementations for all callbacks, delegating to WebsockexNova.Defaults.*
  • Allows you to override any callback by implementing it in your module
  • Imports necessary aliases for convenient access to behaviors and defaults

Examples

Simple adapter with custom connection info

defmodule MyApp.EchoAdapter do
  use WebsockexNova.Adapter

  @impl WebsockexNova.Behaviors.ConnectionHandler
  def connection_info(opts) do
    {:ok, Map.merge(%{
      host: "echo.websocket.org",
      port: 443,
      path: "/",
      transport: :tls
    }, opts)}
  end
end

Financial trading adapter with authentication

defmodule MyApp.TradingAdapter do
  use WebsockexNova.Adapter

  @impl WebsockexNova.Behaviors.ConnectionHandler
  def connection_info(opts) do
    {:ok, Map.merge(%{
      host: "api.exchange.com",
      port: 443,
      path: "/ws/v2",
      transport: :tls
    }, opts)}
  end

  @impl WebsockexNova.Behaviors.AuthHandler
  def authenticate(conn, auth_config, state) do
    # Send authentication message
    auth_msg = %{
      method: "auth",
      params: %{
        api_key: auth_config.api_key,
        api_secret: auth_config.api_secret
      }
    }
    WebsockexNova.Client.send_message(conn, auth_msg)
    {:ok, state}
  end

  @impl WebsockexNova.Behaviors.MessageHandler
  def handle_message(%{"method" => "auth", "result" => true}, state) do
    # Authentication successful
    {:ok, %{authenticated: true}, Map.put(state, :authenticated, true)}
  end
  def handle_message(message, state) do
    # Decode JSON messages
    with {:ok, decoded} <- Jason.decode(message) do
      {:ok, decoded, state}
    else
      _ -> {:error, :invalid_json, state}
    end
  end
end

Gaming adapter with custom subscription handling

defmodule MyApp.GameAdapter do
  use WebsockexNova.Adapter
  alias WebsockexNova.Message.SubscriptionManager

  @impl WebsockexNova.Behaviors.SubscriptionHandler
  def handle_subscription(channel, opts, conn, state) do
    # Custom subscription format for game events
    subscribe_msg = %{
      action: "subscribe",
      channel: channel,
      params: opts || %{}
    }

    case WebsockexNova.Client.send_message(conn, subscribe_msg) do
      {:ok, _} ->
        manager = Map.get(state, :subscription_manager, SubscriptionManager.new())
        updated_manager = SubscriptionManager.add_subscription(manager, channel, opts)
        {:ok, %{}, Map.put(state, :subscription_manager, updated_manager)}
      error ->
        error
    end
  end

  @impl WebsockexNova.Behaviors.MessageHandler
  def handle_message(%{"type" => "game_event"} = message, state) do
    # Handle game-specific events
    {:ok, message, state}
  end
  def handle_message(message, state) do
    DefaultMessageHandler.handle_message(message, state)
  end
end

Available Behaviors

The macro automatically implements these behaviors with default implementations:

Note: LoggingHandler, MetricsCollector, and RateLimitHandler are not included by default since they're typically configured at the client level rather than the adapter level.

Tips

  1. Only override the behaviors you need to customize
  2. Call the default implementations when you want to extend rather than replace behavior
  3. Use pattern matching in message handlers for efficient message routing
  4. Store adapter-specific state in the adapter_state field of the connection

See the WebsockexNova.Examples.AdapterDeribit module for a complete real-world example.