ZenWebsocket

Hex.pmHex DocsLicense

A robust WebSocket client library for Elixir, built on Gun transport for production-grade reliability. Designed for financial APIs with automatic reconnection, comprehensive error handling, and real-world testing.

Features

Installation

Add zen_websocket to your dependencies in mix.exs:

def deps do
[
{:zen_websocket, "~> 0.1"}
]
end

Quick Start

Basic Connection

# Connect to a WebSocket endpoint (use your actual endpoint)
{:ok, client} = ZenWebsocket.Client.connect("wss://api.example.com/ws", [
timeout: 5000,
heartbeat_interval: 30000
])
# Send a message
{:ok, _} = ZenWebsocket.Client.send_message(client, %{action: "ping"})
# Receive messages in your process
receive do
{:websocket_message, message} ->
# Process the incoming message
handle_message(message)
after
5_000 -> {:error, :timeout}
end
# Close the connection
:ok = ZenWebsocket.Client.close(client)

Subscription Management

ZenWebsocket tracks channel subscriptions and automatically restores them after reconnection:

# Subscribe to channels
{:ok, client} = ZenWebsocket.Client.connect("wss://api.example.com/ws")
:ok = ZenWebsocket.Client.subscribe(client, ["ticker.BTC", "trades.BTC"])
# Subscriptions are automatically tracked when confirmations arrive
# On reconnect, tracked subscriptions are restored automatically

What the library handles:

What your client needs to handle:

Configuration:

# Disable automatic subscription restoration
{:ok, client} = ZenWebsocket.Client.connect("wss://api.example.com/ws",
restore_subscriptions: false
)

For more detailed examples, see our working examples with fully tested implementations:

See the Examples Guide for complete code samples and usage patterns.

Deribit Integration

# Configure Deribit credentials
config = %{
client_id: System.get_env("DERIBIT_CLIENT_ID"),
client_secret: System.get_env("DERIBIT_CLIENT_SECRET"),
test_mode: true
}
# Start the supervised adapter
{:ok, adapter} = ZenWebsocket.Examples.DeribitGenServerAdapter.start_link(config)
# Subscribe to market data
{:ok, _} = ZenWebsocket.Examples.DeribitGenServerAdapter.subscribe(
adapter,
["book.BTC-PERPETUAL.raw", "trades.BTC-PERPETUAL.raw"]
)
# Send a custom JSON-RPC request (e.g., place an order)
{:ok, response} = ZenWebsocket.Examples.DeribitGenServerAdapter.send_request(
adapter,
"private/buy",
%{
instrument_name: "BTC-PERPETUAL",
amount: 10,
type: "limit",
price: 50000
}
)

Architecture

ZenWebsocket follows a modular architecture with clear separation of concerns:

ZenWebsocket.Client # Main client interface
ZenWebsocket.Config # Configuration management
ZenWebsocket.Frame # WebSocket frame handling
ZenWebsocket.Reconnection # Automatic reconnection logic
ZenWebsocket.MessageHandler # Message parsing and routing
ZenWebsocket.ErrorHandler # Error categorization
ZenWebsocket.RateLimiter # API rate limiting
ZenWebsocket.JsonRpc # JSON-RPC 2.0 protocol
ZenWebsocket.HeartbeatManager # Heartbeat lifecycle management
ZenWebsocket.SubscriptionManager # Subscription tracking and restoration
ZenWebsocket.RequestCorrelator # Request/response correlation tracking

Platform Integration

The library includes a complete Deribit adapter as a reference implementation. To integrate with other platforms:

  1. Create an adapter module following the Deribit pattern
  2. Implement platform-specific authentication
  3. Handle platform message formats
  4. Add comprehensive tests against the real API

See lib/zen_websocket/examples/deribit_adapter.ex for a complete example.

Documentation

Comprehensive guides are available in the docs/guides/ directory:

GuideDescription
Building AdaptersCreate platform adapters with heartbeat, auth, and reconnection patterns
Performance TuningConfigure timeouts, rate limiting, and memory for your use case
Troubleshooting ReconnectionDebug connection issues and reconnection logic

See the full HexDocs documentation for API reference and module documentation.

Configuration Options

OptionDescriptionDefault
urlWebSocket endpoint URLrequired
headersCustom headers for connection[]
timeoutConnection timeout in milliseconds5000
retry_countMaximum reconnection attempts3
retry_delayInitial retry delay in milliseconds1000
heartbeat_intervalPing interval in milliseconds30000
reconnect_on_errorEnable automatic reconnectiontrue
restore_subscriptionsRestore subscriptions after reconnecttrue
debugEnable verbose debug loggingfalse

Debug Logging

Debug logging is disabled by default to keep library output quiet. Enable it for troubleshooting connection issues:

# Enable debug logging for troubleshooting
{:ok, client} = ZenWebsocket.Client.connect("wss://example.com", debug: true)

When enabled, you'll see detailed logs for connection establishment, WebSocket upgrades, frame handling, heartbeats, and reconnection attempts.

Using Debug in Custom Adapters:

If you're building a custom adapter or extension, use ZenWebsocket.Debug.log/2 with the Config struct:

alias ZenWebsocket.Debug
# Always pass the Config struct (not the full state map)
Debug.log(config, "Custom adapter initialized")
Debug.log(state.config, "Processing message: #{inspect(msg)}")

The function is a no-op when debug: false (the default), so you can leave debug statements in production code without performance impact.

Testing Philosophy

This library uses real API testing exclusively. No mocks or stubs - every test runs against actual WebSocket endpoints or local test servers. This ensures the library handles real-world conditions including network latency, connection drops, and API quirks.

# Run all tests
mix test
# Run with coverage
mix test --cover
# Run quality checks
mix check

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Write tests using real APIs (no mocks)
  4. Ensure all quality checks pass (mix check)
  5. Commit your changes
  6. Push to the branch
  7. Open a Pull Request

Development Commands

mix compile # Compile the project
mix test # Run test suite
mix lint # Run Credo analysis
mix typecheck # Run Dialyzer
mix docs # Generate documentation
mix check # Run all quality checks

License

This project is licensed under the MIT License.

Acknowledgments

Built for the Elixir community by ZenHive.