# `Host.Router`
[🔗](https://github.com/aaronrussell/beamlet/blob/v0.1.0/lib/host/router.ex#L1)

Mount your modules at URLs your beamlet serves.

`live/2` mounts a LiveView page; `get/3`, `post/3`, `put/3`,
`patch/3` and `delete/3` mount a controller action for that verb.
Routes persist across restarts, and every route is public: anyone
who can reach your beamlet can request it. Redefining a mounted
module with `replace: true` updates what its routes serve; there
is no need to unmount and remount.

Two words to keep apart. A *path* is what you choose and what
every function here takes: `"/todos"`, never carrying a prefix the
operator may have configured. A *URL* is what you give people or
external services; `url/1` builds it. In a template, `~p"/todos"`
(imported by `use Host.Web`) turns a path into the browser path a
link needs. A path whose first segment is `beamlet` is your
beamlet's own (`/beamlet/login`, `/beamlet/mcp`) and cannot be
mounted.

Mounting prints the route and its URL and returns `:ok`;
`unmount/1` prints what it removed. `print_routes/0` prints the
route table. `call/4` calls a mounted path in this process and
returns the response as data:

    call(:get, "/todos")
    #=> %{status: 200, headers: %{"content-type" => "text/html; charset=utf-8", ...},
    #     body: "<!DOCTYPE html>..."}

Mounting and unmounting act as you, the token behind the `eval`,
and a route records who mounted it. A served route acts as nobody:
inside one, these verbs and `Host.Code` raise, whether the request
came from a browser or through `call/4`, which sets your principal
aside for the duration of the request so the two behave the same.

# `verb`

```elixir
@type verb() :: :get | :post | :put | :patch | :delete
```

An HTTP verb a route answers.

# `call`

```elixir
@spec call(verb(), String.t(), map() | String.t() | nil, keyword()) :: %{
  status: pos_integer(),
  headers: %{required(String.t()) =&gt; String.t()},
  body: term()
}
```

Calls a mounted route and returns its response, e.g.
`call(:get, "/todos")`.

Or `call(:post, "/hooks/github", %{"action" => "opened"})`. `path`
is the path you mounted. `data` is a map or a string: on GET a map
becomes the query string; on other verbs a map is sent as a JSON
body and a string as the raw body. Pass
`headers: [{"x-hub-signature", sig}]` to add request headers.

Returns `%{status: 201, headers: %{"content-type" => ...}, body: ...}`.
A JSON response body is decoded; any other body is the raw string.
A LiveView page answers GET with its rendered HTML. Anything the
route prints appears in your output. If the route crashes, the
crash is raised here with the route's stacktrace. The call is a
real request: records and files the route creates persist, and
the route acts as nobody, exactly as it does from a browser.

# `delete`

```elixir
@spec delete(String.t(), module(), atom()) :: :ok
```

Mounts a controller action for DELETE at `path`; see `get/3`.

# `get`

```elixir
@spec get(String.t(), module(), atom()) :: :ok
```

Mounts a controller action for GET at `path`, e.g.
`get("/report", Report.Api, :show)`.

The action is called as `show(conn, params)`; the route and its URL
are printed. Controller routes answer JSON/webhook-style requests:
no session, no CSRF, so external services can call them directly.
The module must be a Phoenix controller
(`use Host.Web, :controller`).

# `live`

```elixir
@spec live(String.t(), module(), atom() | nil) :: :ok
```

Mounts a LiveView page at `path`, e.g.
`live("/todos", Todo.PageLive)`.

Prints the route and the URL it is served at. The module must be a
LiveView (`use Host.Web, :live_view`). An optional live action
arrives as `socket.assigns.live_action`, not in the mount params, so
one LiveView can serve several paths:
`live("/todos/new", Todo.PageLive, :new)`.

# `patch`

```elixir
@spec patch(String.t(), module(), atom()) :: :ok
```

Mounts a controller action for PATCH at `path`; see `get/3`.

# `path`

```elixir
@spec path(String.t()) :: String.t()
```

The browser path for `path`, e.g. `path("/todos")`.

It is `"/todos"` on a beamlet serving at the root, and
`"/app/todos"` on one whose operator fenced agent routes under
`/app`. In a template, `~p"/todos"` is the same thing; use this
form in code.

# `post`

```elixir
@spec post(String.t(), module(), atom()) :: :ok
```

Mounts a controller action for POST at `path`, e.g.
`post("/hooks/github", Hooks.Github, :create)`.

Prints the route and its URL, the one external services should
call. See `get/3` for what a controller route is.

# `print_routes`

```elixir
@spec print_routes() :: :ok
```

Prints the mounted routes.

Verb, path, the module (and action) serving each, and the token
that mounted it, under the base URL they are served from. A route
whose module is gone or no longer fits it is marked as not served.

# `put`

```elixir
@spec put(String.t(), module(), atom()) :: :ok
```

Mounts a controller action for PUT at `path`; see `get/3`.

# `sigil_p`
*macro* 

The `~p` sigil: the browser path for a route path in a template.

For links and forms, `<.link navigate={~p"/todos/#{id}"}>`. It is
`path/1` in sigil form: it prepends the operator's prefix, if any,
and nothing else, so there is no compile-time route check. Comes
imported with `use Host.Web`.

# `unmount`

```elixir
@spec unmount(String.t(), keyword()) :: :ok
```

Unmounts every route at `path`, e.g. `unmount("/todos")`.

The page or action stops being served, and each route removed is
printed. Pass `verb: :post` to remove only that verb's route and
leave the others mounted.

# `url`

```elixir
@spec url(String.t()) :: String.t()
```

The full URL for `path`, e.g. `url("/todos")`.

Something like `"http://localhost:4000/todos"`: what you show
people and register with external services.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
