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 absentUsage
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