impulse-utils
A bunch of fullstack utils.
MessagePack support
With impulse-utils, you can:
- parse MsgPack at backend with
impulse_utils::requests::MsgPackParserextension trait (salvo::Request) - send MsgPack from backend with
msgpack!(data)macro - parse MsgPack at client with
impulse_utils::responses::MsgPackResponseextension trait (reqwest::Response) - send MsgPack from client with
impulse_utils::requests::MsgPackRequestextension trait (reqwest::RequestBuilder)
Example:
/// server-side
async
/// client-side
async
For MsgPack ser/de, impulse-utils uses rmp_serde::to_vec and rmp_serde::from_slice methods.
SIMD JSON support by sonic-rs
impulse-utils provide:
- parse JSON at backend with
impulse_utils::requests::SimdJsonParserextension trait (salvo::Request) - send JSON from backend with
json!(data)macro
[!NOTE] To enable SIMD ser/de for JSON, compile your project with compiler option
-C target-cpu=native.
Unified responses and error handling at the backend
In Salvo and Server Kit, you're writing backend routes by one of three ways:
/// like this...
async
/// or like this
async + use<>
/// or like this
async
Only the last one provides you a ? way to expose server errors.
impulse-utils provide unified type MResult<T> for specifying response type while using ServerError for errors.
impulse_utils::results::MResult
MResult allows you to specify these response types:
- just OK 200 empty response:
MResult<OK>andok!()at the end of a route - plain text:
MResult<Plain>andplain!(text) - HTML from string:
MResult<Html>andhtml!(text) - any file:
MResult<File>andfile_upload!(pathbuf, filename)(supports file caching withETagandCache-Control) - JSON:
MResult<Json<T>>andjson!(object)(implemented bysonic-rswith SIMD support) - MsgPack:
MResult<MsgPack<T>>andmsgpack!(object)
Resulting macros are used in TRACE log level to show you what you're sending as a response and from what function.
Examples:
async
async
impulse_utils::errors::ServerError
ServerError also implements salvo::Writer, but it does more. Internally it contains HTTP status code to return with (500 by default), public error message and the list of private error messages. ServerError is designed to hide implementation details exposed with errors by separation into two types: public errors and private errors.
ServerError on response converts into simple JSON:
This is how your clients will get error messages.
If public_msg field is set, your server will respond with value of this field. You can set public_msg several times, and client will get only one:
from_public
.with_public
.with_public
.with_500
.bail?;
/// Client will see:
///
/// ```json
/// {"err":"Internal server error, call 911"}
/// ```
///
/// Backend logs:
///
/// ```
/// 2025-05-11T00:24:49.474405Z ERROR Error: `500` status code
/// Error message: "Internal server error, call 911"
/// Caused by: Database error
/// Caused by: Bad data inside SQL
/// ```
Example with any error type that implements std::error::Error trait:
verify_sign
.map_err?;
Or Option:
let user_data = kv
.
.await?
.ok_or?;
ServerError is used by MResult<T> type and supports both Salvo and Server Kit frameworks.
ExplicitServerWrite trait
This trait is designed to use only &mut Response on write instead of a full signature of salvo::Writer trait:
async
You can use it, for example, when you're using any reference to the depot data and responding with this data:
// Usually you responds by `HTTP 200 OK`, but sometimes need to also include some data that referring to `depot`.
// Of course, you can just `.clone()` your value, but `ExplicitServerWrite` needs no allocations compared to cloning.
async
This trait is implemented to all exposed by impulse-utils response types above except file_upload! (internally it needs req: &mut Request to compare ETag inside headers to allow file caching).
impulse_utils::results::CResult and impulse_utils::errors::ClientError
CResult<T> is a Result<T, ClientError>. ClientError was designed to be simple client error which you can just console.log at client-side.
Example:
let resp = new
.post
.json
.send
.await
.map_err?
.error_for_status
.map_err?;
Redirect or collect server errors
If you have several backends and communicate between them by reqwest, you may be considering of usage redirect_server_error method on reqwest::Response to directly redirect any error, especially ErrorResponse (public part of ServerError):
let resp = new
.post
.json
.send
.await
.redirect_server_error // if status >= 400, it will throw `Err::<ServerError>`; if no, returns `Ok::<reqwest::Response>`
.await?
.
.await?;
But, if you're using reqwest on the client-side, you may want to use collect_server_error to collect CResult instead of MResult:
let resp = new
.post
.json
.send
.await
.collect_server_error // if status >= 400, it will throw `Err::<ClientError>`; if no, returns `Ok::<reqwest::Response>`
.await?
.
.await?;
[!NOTE]
redirect_server_errormethod is available withreqwestandmresultfeatures.
collect_server_errormethod is available withreqwestandcresultfeatures.