RpcElixir.Middleware behaviour (elixir_ts_rpc v0.0.1)

Copy Markdown View Source

Behaviour for procedure middleware.

A middleware is a module that transforms a %RpcElixir.Resolution{} and returns a (possibly modified) resolution. Middleware compose around the dispatcher and may:

  • read or update ctx via Resolution.put_ctx/3, Resolution.assign/3, Resolution.put_private/3
  • halt the chain via Resolution.halt/2, which short-circuits remaining middleware and the handler
  • leave the resolution untouched

Declaring middleware

Middleware are attached to a procedure in the router DSL:

procedure "users.update", &Hello.Users.update/2,
  middleware: [
    RpcElixir.Middleware.Assign,
    {RpcElixir.Middleware.Assign, key: :source, value: :api}
  ]

Each entry is either a bare module or a {module, opts} tuple. Bare modules receive [] as opts.

Field ownership

Middleware must not write to :result directly — that field is owned by the dispatcher. Use halt/2 to short-circuit with an error.

Implementing a middleware

defmodule MyApp.Middleware.RequireUser do
  @behaviour RpcElixir.Middleware

  @impl true
  def call(%RpcElixir.Resolution{ctx: %{user: nil}} = res, _opts) do
    RpcElixir.Resolution.halt(res, :unauthenticated)
  end

  def call(res, _opts), do: res
end

Summary

Callbacks

Declares the error codes this middleware may pass to halt/2, so codegen can fold them into the declared error type of every procedure the middleware wraps.

Types

opts()

@type opts() :: term()

Callbacks

call(t, opts)

rpc_error_codes(opts)

(optional)
@callback rpc_error_codes(opts()) :: [atom()]

Declares the error codes this middleware may pass to halt/2, so codegen can fold them into the declared error type of every procedure the middleware wraps.

This is what lets the generated client know a cross-cutting error like :unauthorized is possible on an endpoint even though no handler @spec mentions it — the procedure's generated TypeScript error type and its runtime .isError codes both pick these up. Optional — middleware that doesn't implement it contributes no codes (the codegen behaves exactly as before).

Receives the middleware's opts so a code set can depend on configuration.

@impl true
def rpc_error_codes(_opts), do: [:unauthorized]