-
Hey all. I forked the hacker news example to start our project and enabled rkyv support (check out #577) - everything works! (6ms API response times 🎉). However, it isn't as blazingly fast as it should be, as it's using a new reqwest client and, therefore, connection on every fetch it does while SSR'ing. This is not great. Here's some code (api.rs): use bytecheck::CheckBytes;
use leptos::{Scope, Serializable};
use rkyv::{
de::deserializers::SharedDeserializeMap, ser::serializers::AllocSerializer,
validation::validators::DefaultValidator, Archive, Deserialize, Serialize,
from_bytes, to_bytes
};
#[cfg(not(feature = "ssr"))]
use js_sys::Uint8Array;
#[cfg(not(feature = "ssr"))]
pub async fn fetch<T, K, const N: usize>(cx: Scope, path: &str, body: K) -> Option<T>
where
T: Serialize<AllocSerializer<1024>>,
T: Serializable + Archive,
T::Archived: for<'b> CheckBytes<DefaultValidator<'b>> + Deserialize<T, SharedDeserializeMap>,
K: Serialize<AllocSerializer<N>>,
{
let abort_controller = web_sys::AbortController::new().ok();
let abort_signal = abort_controller.as_ref().map(|a| a.signal());
let bytes = gloo_net::http::Request::get(path)
.abort_signal(abort_signal.as_ref())
.body(Uint8Array::from(to_bytes(&body).ok()?.as_slice()))
.send()
.await
.map_err(|e| log::error!("{e}"))
.ok()?
.binary()
.await
.ok()?;
// abort in-flight requests if the Scope is disposed
// i.e., if we've navigated away from this page
leptos::on_cleanup(cx, move || {
if let Some(abort_controller) = abort_controller {
abort_controller.abort()
}
});
from_bytes::<T>(&bytes).ok()
}
#[cfg(feature = "ssr")]
pub async fn fetch<T, K, const N: usize>(_cx: Scope, path: &str, body: K) -> Option<T>
where
T: Serialize<AllocSerializer<1024>>,
T: Serializable + Archive,
T::Archived: for<'b> CheckBytes<DefaultValidator<'b>> + Deserialize<T, SharedDeserializeMap>,
K: Serialize<AllocSerializer<N>>,
{
let client = reqwest::Client::new();
let start = std::time::Instant::now();
let bytes = client
.get(path)
.body(to_bytes(&body).ok()?.to_vec())
.send()
.await
.map_err(|e| log::error!("{e}"))
.ok()?
.bytes()
.await
.ok()?;
log::info!("fetch took {:?}", start.elapsed());
from_bytes::<T>(&bytes).ok()
} Everything is great and working quickly, apart from reqwest creating a new connection on every render. Here are some server logs: # reqwest starts connection open
2023-02-26T15:04:05.183696Z DEBUG reqwest::connect: starting new connection: http://localhost:8080/
2023-02-26T15:04:05.183893Z DEBUG hyper::client::connect::dns: resolving host="localhost"
2023-02-26T15:04:05.184560Z DEBUG hyper::client::connect::http: connecting to [::1]:8080
2023-02-26T15:04:05.495580Z DEBUG hyper::client::connect::http: connecting to 127.0.0.1:8080
2023-02-26T15:04:05.511344Z DEBUG hyper::client::connect::http: connected to 127.0.0.1:8080
# reqwest finished connection open in 328ms
# send the actual request for the page render
2023-02-26T15:04:05.511935Z DEBUG hyper::proto::h1::io: flushed 102 bytes
2023-02-26T15:04:05.518316Z DEBUG hyper::proto::h1::io: parsed 3 headers
2023-02-26T15:04:05.518517Z DEBUG hyper::proto::h1::conn: incoming body is content-length (80 bytes)
2023-02-26T15:04:05.518664Z DEBUG hyper::proto::h1::conn: incoming body completed
# reqwest completed in 6.9ms
2023-02-26T15:04:05.518866Z DEBUG hyper::client::pool: pooling idle connection for ("http", localhost:8080)
2023-02-26T15:04:05.519106Z INFO valera_web::api: fetch took 335.4284ms Ideally, I would preconnect to the API server when Actix boots up. We do a similar thing on our API server (also Actix): fn main() -> {
...
let state = web::Data::new(AppState {
...
reqwest: reqwest::Client::new(),
...
});
preconnect(&state.reqwest, "rest.nexmo.com").await;
...
}
async fn preconnect(client: &reqwest::Client, domain: &str) {
debug!("Preconnecting to {}", domain);
let _ = client.head(format!("https://{}", domain)).send().await;
} Here, the reqwest client is under an Arc at state.reqwest (i.e. one reqwest client for all threads). state,reqwest is then provided as part of the app state to each route handler who can access it. How can we do this alongside leptos (I'm guessing implementation will be server-implementation specific)? Thank you so much for your attention and participation. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 5 replies
-
The dead-simple solution would be to create the The slightly-more-complicated one would be to provide it with the context API by using |
Beta Was this translation helpful? Give feedback.
The dead-simple solution would be to create the
reqwest
connection in themain
function inmain.rs
and store it in a staticOnceCell
, then access it in the server API.The slightly-more-complicated one would be to provide it with the context API by using
leptos_routes_with_context
instead ofleptos_routes
and providing the connection withprovide_context()