DEV Community

Cover image for How To Improve React App Performance with SSR and Rust [Part II: Rust Web Server]
Olex Tkachuk
Olex Tkachuk

Posted on • Edited on • Originally published at hackernoon.com

1

How To Improve React App Performance with SSR and Rust [Part II: Rust Web Server]

Continue creating boosted Server Side Rendering implementation. In the first part we prepared ReactJS Application with SSR script that lets choose the best technologies for our Web Server.

Choosing Tech Stack for the SSR Web server

Rust has the most powerful combination of safety and hight speed for today (you can check out here why - www.rust-lang.org). In addition, Actix-web framework is the fastest one according TechEmpower Framework Benchmark.

So, let’s use the best technologies for our forced Web Server.

Setup Rust App

It is easy to setup Rust on your computer - just go to rustup.rs website and install rustup CLI in just one step.

Next step is initialisation a new app. Cargo, Rust package manager can help with it: run cargo init inside repository folder.

The Cargo.toml file for each package is called its manifest. It contains settings and dependencies. New one should look like that:

[package]
name = "rust-ssr-webserver"
version = "0.1.0"
authors = ["Alex Tkachuk <alex@pagespeed.green>"]
edition = "2018"
Enter fullscreen mode Exit fullscreen mode

Now we can add our dependencies:

[dependencies]

actix-web = { version = "^2.0.0", features = ["rustls"] }
actix-rt = "^1.0.0"
actix-files = "^0.2.1"
env_logger = "^0.7.1"
futures = "^0.3.4"
mime_guess = "^2.0.1"
serde_json = "^1.0.40"
lazy_static = "^1.4.0"
rustls = "^0.16.0"
Enter fullscreen mode Exit fullscreen mode

Using actix-web is quite easy to create web server. Firstly, we need to read SSL keys for supporting HTTPS and HTTP/2:

let mut config = ServerConfig::new(NoClientAuth::new());
let cert_file = &mut BufReader::new(File::open("cert.pem").unwrap());
let key_file = &mut BufReader::new(File::open("key.pem").unwrap());
let cert_chain = certs(cert_file).unwrap();
let mut keys = rsa_private_keys(key_file).unwrap();

config.set_single_cert(cert_chain, keys.remove(0)).unwrap();
Enter fullscreen mode Exit fullscreen mode

Be aware of using unwrap() in production - if it fails, the app will crash (panic).

We are ready for actually Web Server code now:

HttpServer::new(|| {
        App::new()
            .wrap(middleware::Logger::default())
            .service(Files::new("/static", "static"))
            .default_service(
                web::resource("")
                    .route(web::get().to(index))
                    .route(
                        web::route()
                            .guard(guard::Not(guard::Get()))
                            .to(|| HttpResponse::MethodNotAllowed()),
                    ),
            )
    })
    .bind_rustls("0.0.0.0:3001", config)?
    .run()
    .await
Enter fullscreen mode Exit fullscreen mode

This line: .service(Files::new("/static", "static") is for serving all static asserts that we created by running npm build:ssr in previous article - (Part I: SSR). For handling HTML-files requests from ReactJs App we need to use .default_service() with serving all routes by using web::resource("") with empty string. The index function is handler for such requests:

async fn index(req: HttpRequest) -> impl Responder {
    let path_req = req.match_info().query("tail").get(1..).unwrap_or_default().trim().clone();
    let path = if path_req.len() == 0 {
        "home_page"
    } else {
        match ROUTES.get(path_req) {
            Some(r) => r,
            None => "index"
        }
    };

    match std::fs::File::open(format!("static/{}.html", path)) {
        Ok(mut file) => {
            let mut contents = String::new();
            file.read_to_string(&mut contents).unwrap_or_default();

            HttpResponse::Ok()
                .content_type("text/html; charset=utf-8")
                .header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate")
                .header("pragma", "no-cache")
                .header("x-ua-compatible", "IE=edge, Chrome=1")
                .body(contents)
        },
        Err(e) => {
            // error handling
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The function implements the routing logic, so our web server can return right HTML file (React server rendered) by a route. The ROUTES constant is basically contains data from routes.json that was generated on React side.

One improvement that you can do for this implementation is using a cash instead of directly reading a file from disk.
Finally, copy folder dist/web to static, obviously, it should be automatic step, and run our Web Server by cargo r.

Full example is located in the GitHub repository.

The final article of Server Side Rendering will be about testing performance between this solution vs Node.js


You can check how fast this approach in production by getting Google PageSpeed Insights Performance score for PageSpeed Green website.

Happy coding!


Runner H image

Check out what they built with Runner H "AI Agent Prompting" 👀

From culinary assistants to sports analysis tools to hackathon discovery agents, our submissions were full of diverse use cases!

Read more →

Top comments (0)

Feature flag article image

Create a feature flag in your IDE in 5 minutes with LaunchDarkly’s MCP server 🏁

How to create, evaluate, and modify flags from within your IDE or AI client using natural language with LaunchDarkly's new MCP server. Follow along with this tutorial for step by step instructions.

Read full post

👋 Kindness is contagious

Dive into this thoughtful piece, beloved in the supportive DEV Community. Coders of every background are invited to share and elevate our collective know-how.

A sincere "thank you" can brighten someone's day—leave your appreciation below!

On DEV, sharing knowledge smooths our journey and tightens our community bonds. Enjoyed this? A quick thank you to the author is hugely appreciated.

Okay