Core dispatch pipeline: lookup → middleware → validate input → invoke handler → validate output → serialize.
Every transport funnels through dispatch/4 — the HTTP Plug and in-process
callers alike. It threads a %RpcElixir.Resolution{} through the procedure's
middleware chain and the inner pipeline. The returned resolution carries the
outcome on its :result field. So middleware that wraps dispatch/4 can
observe and transform results uniformly.
Typed handler errors
A handler returns {:error, reason}. The dispatcher promotes reason to a
top-level %RpcError{} and stamps source: :domain unless the handler set
one. Promotion never sets :status, except in the last case below.
The transport derives the status for any promoted error whose :status is
nil. It reads the framework status map in RpcElixir.RpcError. Unknown
codes fall back to a generic status. Return %RpcError{status: 422} from a
handler to choose the status yourself.
reasonis an atom:code = reason,message = Atom.to_string(reason),details = nil.reasonis a plain map (not a struct) with an atom:code:code = reason.code,message = reason[:message] || Atom.to_string(code),details = reasonminus:codeand:message, ornilif empty.reasonis already an%RpcError{}with a non-nil:source: passed through unchanged. Anil:sourceis stampedsource: :domain.- Anything else — structs, tuples, keyword lists, lists: wrapped as
%RpcError{code: :handler_error, details: %{kind: :error, reason: inspect(reason)}}at status 500. That is the:handler_errordefault inRpcElixir.RpcError.framework_errors/0. These are framework-level "handler returned something unexpected" cases.
This contract matches the JS Error shape. On the TypeScript client, a typed
error populates err.code, err.message, and err.details. err.message
shows up in stack traces and console.error. See
Handling errors for the user-facing guide.
Summary
Functions
Dispatches a procedure call against router, returning the resolution with
:result populated.
Types
@type result() :: {:ok, term()} | {:error, RpcElixir.RpcError.t()}
Final result populated on Resolution.result after dispatch.
Functions
@spec dispatch(module(), String.t(), map(), RpcElixir.Resolution.t()) :: RpcElixir.Resolution.t()
Dispatches a procedure call against router, returning the resolution with
:result populated.
- An already-halted resolution is returned as-is.
- An unknown path yields a
:procedure_not_foundRpcError. No middleware runs. - Otherwise the middleware chain wraps the inner pipeline. Any middleware may halt the resolution to short-circuit.