arizona_route (arizona v0.4.0)

View Source

The kinds of route an Arizona route list holds, independent of any server.

An arizona_server adapter compiles each route into its server's own routing. kind/1 names what a route is, so an adapter dispatches on one atom instead of re-matching every tuple shape, and refuses a route it cannot serve with unsupported/2 -- at route compilation, before a request could reach it, the way arizona_controller:dispatch/3 refuses a reply kind the adapter cannot send.

KindRoute
live{live, Path, Handler, Opts}
ws{ws, Path, Opts}
asset{asset, Path, Source}, {asset, Path, Source, Opts}
controller{Verb, Path, Handler, Opts}, {match, Spec, Path, Handler, Opts}
mcp{mcp, Path, Handler, Opts}
reload{reload, Path, Opts}

live, ws, asset, and controller are what an application is built from, and every adapter serves them. mcp (the Model Context Protocol endpoint) and reload (the dev hot-reload event stream) are capabilities.

Route rules

What a route means does not depend on the server, so the rules live here and every adapter's router compiles from them rather than restating them:

  • methods/1 -- the method allowlist (GET implies HEAD; '*' is any).
  • steps/1 -- the request pipeline, with the CSRF check_origin step first unless the route opts out (check_origin => false).
  • transforms/2 -- the HTTP response transforms, validated, with compression outermost and on by default for pages and assets.
  • live/2, controller/1, asset_dir/1 -- what each kind is served with.

Summary

Functions

The directory an asset route serves, a priv_dir source resolved.

What a controller route runs: the arizona_controller:route() that arizona_controller:dispatch/3 takes. The mcp and reload kinds are controllers too, behind no pipeline: arizona_mcp_handler gates its own requests (origin, peer, auth hook) and answers its own 405, with the route's handler module folded into its state; arizona_reload_controller is a dev endpoint the page it serves opens itself.

Renders this module's errors. Picked up by erl_error via the error_info annotation.

The kind of Route. Raises {unknown_route, Route} for a term that is no route: a route list is plain data a sys.config can spell by hand, so a typo lands here rather than at a constructor.

What a live route renders with: its handler, bindings, on_mount, layouts, the pipeline steps/1, and the listener's error_page (default {arizona_error_page, render}). An adapter's arizona_req:resolve_route/3 returns these, minus handler, as the route options.

The method allowlist of Route. A page or asset answers GET and HEAD; a controller its declared verbs, uppercased, HEAD added beside GET; a {match, '*', ...} controller and the ws and mcp kinds any; a reload route is an event stream, GET and HEAD. '*' inside a method list raises wildcard_in_method_list: there it would match no real method.

The path pattern of Route.

The request pipeline of a live or controller route: its middlewares steps, in order, behind the CSRF check_origin step unless the route sets check_origin => false. HTTP response transforms are not steps (transforms/2).

The HTTP response transforms of Route, in the order they wrap the exchange (first outermost). Each is validated, raising {unknown_http_transform, T} for a hand-spelled typo. Compression is outermost wherever it is written, so an etag always hashes the uncompressed body; a live or asset route also gets it by default unless BuildOpts sets compress => false (an explicit arizona_middleware:compress() on the route still applies).

Raises {unsupported_route, Kind, Adapter}: what an adapter does, while compiling its routes, with a route kind its server cannot serve -- so the listener refuses to start instead of answering that path with something else.

Types

build_opts()

-nominal build_opts() :: #{compress => boolean(), error_page => {module(), atom()}, _ => term()}.

kind()

-nominal kind() :: live | ws | asset | controller | mcp | reload.

methods()

-nominal methods() :: [binary()] | any.

Functions

asset_dir(Route)

-spec asset_dir(Route) -> file:filename_all() when Route :: term().

The directory an asset route serves, a priv_dir source resolved.

controller(Route)

-spec controller(Route) -> arizona_controller:route() when Route :: term().

What a controller route runs: the arizona_controller:route() that arizona_controller:dispatch/3 takes. The mcp and reload kinds are controllers too, behind no pipeline: arizona_mcp_handler gates its own requests (origin, peer, auth hook) and answers its own 405, with the route's handler module folded into its state; arizona_reload_controller is a dev endpoint the page it serves opens itself.

format_error(Reason, Stacktrace)

-spec format_error(Reason, Stacktrace) -> ErrorInfo
                      when
                          Reason :: term(), Stacktrace :: [tuple()], ErrorInfo :: #{general := iolist()}.

Renders this module's errors. Picked up by erl_error via the error_info annotation.

kind(Route)

-spec kind(Route) -> kind() when Route :: term().

The kind of Route. Raises {unknown_route, Route} for a term that is no route: a route list is plain data a sys.config can spell by hand, so a typo lands here rather than at a constructor.

live(Route, BuildOpts)

-spec live(Route, BuildOpts) -> Live
              when
                  Route :: term(),
                  BuildOpts :: build_opts(),
                  Live ::
                      #{handler := module(),
                        bindings := map(),
                        on_mount := arizona_live:on_mount(),
                        layouts := [arizona_render:layout()],
                        middlewares := [arizona_middleware:middleware()],
                        error_page := {module(), atom()}}.

What a live route renders with: its handler, bindings, on_mount, layouts, the pipeline steps/1, and the listener's error_page (default {arizona_error_page, render}). An adapter's arizona_req:resolve_route/3 returns these, minus handler, as the route options.

methods(Route)

-spec methods(Route) -> methods() when Route :: term().

The method allowlist of Route. A page or asset answers GET and HEAD; a controller its declared verbs, uppercased, HEAD added beside GET; a {match, '*', ...} controller and the ws and mcp kinds any; a reload route is an event stream, GET and HEAD. '*' inside a method list raises wildcard_in_method_list: there it would match no real method.

path(Route)

-spec path(Route) -> binary() when Route :: term().

The path pattern of Route.

steps(Route)

-spec steps(Route) -> [arizona_middleware:middleware()] when Route :: term().

The request pipeline of a live or controller route: its middlewares steps, in order, behind the CSRF check_origin step unless the route sets check_origin => false. HTTP response transforms are not steps (transforms/2).

transforms(Route, BuildOpts)

-spec transforms(Route, BuildOpts) -> [arizona_middleware:http_transform()]
                    when Route :: term(), BuildOpts :: build_opts().

The HTTP response transforms of Route, in the order they wrap the exchange (first outermost). Each is validated, raising {unknown_http_transform, T} for a hand-spelled typo. Compression is outermost wherever it is written, so an etag always hashes the uncompressed body; a live or asset route also gets it by default unless BuildOpts sets compress => false (an explicit arizona_middleware:compress() on the route still applies).

An asset route runs no pipeline, so a step among its middlewares would never run: it raises {asset_middleware_not_transform, Steps}.

unsupported(Kind, Adapter)

-spec unsupported(Kind, Adapter) -> no_return() when Kind :: kind(), Adapter :: module().

Raises {unsupported_route, Kind, Adapter}: what an adapter does, while compiling its routes, with a route kind its server cannot serve -- so the listener refuses to start instead of answering that path with something else.