It is possible to extend Riptide to support more than simple Mutations and Queries. Riptide Handlers let you define custom commands that can be triggered by the client. It's effectively a simple RPC framework. Handlers can be added to your Riptide configuration.

config :riptide,
  handlers: [

When a command is sent up from the client, every handler in this is called in order until one handles the command.


Clients connect to Riptide over a websocket connection and send commands (either cast or calls) that contain an action and a body. Each connection has state that is passed into the handler when it is triggered by a command. The handler can process the command and choose to update the state. The lifecycle looks very similar to Elixir's GenServer.



A call is a command that expects a reply.

defmodule Todo.Auth do
  use Riptide.Command

  def handle_call("todo.login", %{"email" => email, "password" => password}, state) do
    case Todo.User.login(email, password) do
      {:ok, user} -> {:reply, true, %{user: user}}
      {:error, error} -> {:error, error, state}

The valid responses are

  • {:reply, reply, next_state} - Sends the reply to the client and updates the connection state
  • {:error, error, next_state} - Sends the error to the client and updates the connection state



A cast is a command that does not expect a reply.

defmodule Todo.Ping do
  use Riptide.Command

  def handle_cast("todo.ping", _body, state) do
    Riptide.merge!(["user:info", state.user, "last_seen"], :os.system_time(:millisecond))
    {:noreply, state}

A valid response is

  • {:noreply, next_state} - Updates the connection state with next_state