Expand description
§Server Functions
This package is based on a simple idea: sometimes it’s useful to write functions that will only run on the server, and call them from the client.
If you’re creating anything beyond a toy app, you’ll need to do this all the time: reading from or writing to a database that only runs on the server, running expensive computations using libraries you don’t want to ship down to the client, accessing APIs that need to be called from the server rather than the client for CORS reasons or because you need a secret API key that’s stored on the server and definitely shouldn’t be shipped down to a user’s browser.
Traditionally, this is done by separating your server and client code, and by setting up something like a REST API or GraphQL API to allow your client to fetch and mutate data on the server. This is fine, but it requires you to write and maintain your code in multiple separate places (client-side code for fetching, server-side functions to run), as well as creating a third thing to manage, which is the API contract between the two.
This package provides two simple primitives that allow you instead to write co-located, isomorphic server functions. (Co-located means you can write them in your app code so that they are “located alongside” the client code that calls them, rather than separating the client and server sides. Isomorphic means you can call them from the client as if you were simply calling a function; the function call has the “same shape” on the client as it does on the server.)
§#[server]
The #[server]
macro allows you to annotate a function to
indicate that it should only run on the server (i.e., when you have an ssr
feature in your
crate that is enabled).
Important: Before calling a server function on a non-web platform, you must set the server URL by calling
set_server_url
.
#[server]
async fn read_posts(how_many: usize, query: String) -> Result<Vec<Posts>, ServerFnError> {
// do some server-only work here to access the database
let posts = ...;
Ok(posts)
}
// call the function
async {
let posts = read_posts(3, "my search".to_string()).await;
log::debug!("posts = {posts:#?}");
}
If you call this function from the client, it will serialize the function arguments and POST
them to the server as if they were the URL-encoded inputs in <form method="post">
.
Here’s what you need to remember:
- Server functions must be
async
. Even if the work being done inside the function body can run synchronously on the server, from the client’s perspective it involves an asynchronous function call. - Server functions must return
Result<T, ServerFnError>
. Even if the work being done inside the function body can’t fail, the processes of serialization/deserialization and the network call are fallible.ServerFnError
can receive generic errors. - Server functions are part of the public API of your application. A server function is an ad hoc HTTP API endpoint, not a magic formula. Any server function can be accessed by any HTTP client. You should take care to sanitize any data being returned from the function to ensure it does not leak data that should exist only on the server.
- Server functions can’t be generic. Because each server function creates a separate API endpoint, it is difficult to monomorphize. As a result, server functions cannot be generic (for now?) If you need to use a generic function, you can define a generic inner function called by multiple concrete server functions.
- Arguments and return types must be serializable. We support a variety of different encodings, but one way or another arguments need to be serialized to be sent to the server and deserialized on the server, and the return type must be serialized on the server and deserialized on the client. This means that the set of valid server function argument and return types is a subset of all possible Rust argument and return types. (i.e., server functions are strictly more limited than ordinary functions.)
§Server Function Encodings
Server functions are designed to allow a flexible combination of input and output encodings, the set
of which can be found in the codec
module.
Calling and handling server functions is done through the Protocol
trait, which is implemented
for the Http
and Websocket
protocols. Most server functions will use the Http
protocol.
When using the Http
protocol, the serialization/deserialization process for server functions
consists of a series of steps, each of which is represented by a different trait:
IntoReq
: The client serializes theServerFn
argument type into an HTTP request.- The
Client
sends the request to the server. FromReq
: The server deserializes the HTTP request back into theServerFn
type.- The server calls calls
ServerFn::run_body
on the data. IntoRes
: The server serializes theServerFn::Output
type into an HTTP response.- The server integration applies any middleware from
ServerFn::middlewares
and responds to the request. FromRes
: The client deserializes the response back into theServerFn::Output
type.
Re-exports§
pub use error::ServerFnError;
pub use rkyv;
Modules§
- actix
- Actix integration.
- axum
- Axum integration.
- client
- Implementations of the client side of the server function call.
- codec
- Encodings for arguments and results. The serialization/deserialization process for server functions consists of a series of steps, each of which is represented by a different trait:
- error
- Error types and utilities.
- middleware
- Types to add server middleware to a server function.
- mock
- Mocks for the server function backend types when compiling for the client.
- redirect
- Utilities to allow client-side redirects.
- request
- Types and traits for for HTTP requests.
- response
- Types and traits for HTTP responses.
- server
- Implementations of the server side of the server function call.
Macros§
- initialize_
server_ fn_ map - Uses the
inventory
crate to initialize a map between paths and server functions. - server_
fn_ error Deprecated - A helper macro to convert a variety of different types into
ServerFnError
. This should mostly be used if you are implementingFrom<ServerFnError>
forYourError
.
Structs§
- Boxed
Stream - A boxed stream type that can be used with the websocket protocol.
- Bytes
- A cheaply cloneable and sliceable chunk of contiguous memory.
- Http
- The http protocol with specific input and output encodings for the request and response. This is the default protocol server functions use if no override is set in the server function macro
- Server
FnTrait Obj - A trait object that allows multiple server functions that take the same request type and return the same response type to be gathered into a single collection.
- Websocket
- The websocket protocol that encodes the input and output streams using a websocket connection.
Enums§
- Format
- Encode format type
Traits§
- Content
Type - A trait for types with an associated content type.
- Decodes
- A trait for types that can be decoded from a bytes for a response body.
- Encodes
- A trait for types that can be encoded into a bytes for a request body.
- Format
Type - Data format representation
- Protocol
- The protocol that a server function uses to communicate with the client. This trait handles
the server and client side of running a server function. It is implemented for the
Http
andWebsocket
protocols and can be used to implement custom protocols. - Server
Fn - Defines a function that runs only on the server, but can be called from the server or the client.
Type Aliases§
- Middleware
Set - A list of middlewares that can be applied to a server function.