RpcElixir.Handler (elixir_ts_rpc v0.0.1)

Copy Markdown View Source

Captures @spec and @type ASTs at handler-compile time and exposes them via __rpc_specs__/0 / __rpc_types__/0 accessors.

Why this exists

RpcElixir.Router validates handler signatures inside __before_compile__. By default it reads them from the handler's BEAM file via Code.Typespec.fetch_specs/1, which only works after the BEAM is on disk. Inside a single Mix project, the parallel compiler may run the router's compile-time hook before in-progress handler BEAMs are flushed, producing spurious "no @spec" errors.

When a handler does use RpcElixir.Handler, this macro captures the spec ASTs into a generated function. The router (via RpcElixir.Types.FromSpec) prefers that accessor when it exists, and the resulting function-call edge forces the parallel compiler to fully compile the handler module before using it — without requiring the BEAM to be on disk.

Without use RpcElixir.Handler, the framework still works but requires the handler to live in a separate Mix path: dep so its BEAM is on disk first.

Input keys are atoms

The input argument received by every handler function has atom keys (e.g. %{id: "abc"}), never string keys. Pattern-match and access accordingly:

def get(%{id: id}, _ctx), do: ...   # correct
def get(%{"id" => id}, _ctx), do: ... # wrong — key will be absent

Usage

defmodule MyApp.Handlers.Users do
  use RpcElixir.Handler

  @spec list(input :: %{}, ctx :: map()) :: {:ok, %{users: [%{id: String.t()}]}}
  def list(_input, _ctx), do: {:ok, %{users: []}}
end