A procedure is an async server function called from a runtime [expression](macro.expr.html). Use it when browser code needs server resources, such as a database. Each procedure has an HTTP endpoint. **Validate its arguments and authorize access inside the procedure**, since callers can send their own requests.
```rust
use topcoat::{Result, context::Cx, runtime::procedure};
#[procedure]
async fn double(cx: &Cx, value: usize) -> Result<usize> {
Ok(value * 2)
}
```
# Calling Procedures
Inside a runtime expression, call a procedure like an ordinary async function and `.await` its result. Omit `cx` from the call; Topcoat supplies the server request context automatically:
```rust
# use topcoat::{Result, context::Cx, view::*, runtime::{procedure, signal}};
# #[procedure]
# async fn double(cx: &Cx, value: usize) -> Result<usize> { Ok(value * 2) }
# #[component]
# async fn example(cx: &Cx) -> Result<impl View> {
let count = signal(cx, || 1usize);
Ok(view! {
<button
@click=$(async |_e| {
let doubled = double(count.get()).await;
count.set(doubled);
})
>
"double it"
</button>
$(count.get())
})
# }
```
The call sends its arguments to the server and waits for the function's result. Await it in an async context, such as an event handler's `async` closure.
A procedure call in a runtime expression must run only in the browser. The server type-checks the call but panics if it evaluates it during rendering. An event-handler closure, as above, defers the call until a browser event.
# Arguments And Return Type
The function must be `async` and take `cx: &Cx` as its first parameter, even when unused. Additional parameters must use types supported by [`expr!`]. The function must return a [`Result`] whose `Ok` value also uses a supported type. These arguments and the `Ok` value travel between the Rust server and JavaScript in the browser.
Use `cx` to read context values or request data:
```rust
use topcoat::{Result, context::Cx, runtime::procedure};
#[procedure]
async fn search(cx: &Cx, query: String) -> Result<String> {
// Use cx to read shared values or check who made the request.
# let _ = cx;
Ok(query)
}
```
# Errors
Awaiting a call yields the procedure's `Ok` value. If the procedure returns `Err`, the server sends an error response and the browser expression fails. The expression cannot inspect that error. To let the client handle a failure, return it as data, for example with an `Ok` type of `Result<String, String>`.
# Registration
Register a procedure on the [`Router`] before calling it from the browser. It implements [`Route`], so pass its name to `.route()`:
```rust,standalone_crate
use topcoat::{context::Cx, router::module_router};
# use topcoat::{Result, router::Router, runtime::procedure};
# #[procedure]
# async fn double(cx: &Cx, value: usize) -> Result<usize> { Ok(value * 2) }
let router = module_router!().route(double).build();
```
With the `discover` feature enabled, `.discover()` registers all procedures linked into the application.
# Path
Topcoat generates an internal path for each procedure from its name and source location. This path changes when the procedure is renamed or moved. To choose a stable endpoint, pass an absolute path to `#[procedure]`:
```rust
use topcoat::{Result, context::Cx, runtime::procedure};
#[procedure("/api/double")]
async fn double(cx: &Cx, value: usize) -> Result<usize> {
Ok(value * 2)
}
```
The browser calls this procedure with a `POST` request to `/api/double`. Arguments are sent in the request body, so the path cannot contain dynamic or catch-all parameters.
The path follows the router's [path syntax](../router/index.html#paths). Groups affect layer matching but are omitted from the URL. For example, `#[procedure("/(api)/double")]` serves calls at `/double`.
[`Cx`]: ../context/struct.Cx.html
[`Result`]: ../type.Result.html
[`Route`]: ../router/trait.Route.html
[`Router`]: ../router/struct.Router.html
[`expr!`]: macro.expr.html