How To Improve React App Performance with SSR and Rust [Part II: Rust Web Server]
Tags: rust, webdev, webperf, ssr
Published: 2020-02-15 11:53:51

> Continue creating boosted Server Side Rendering implementation. In [the first part](https://dev.to/pagespeed-green/how-to-improve-react-app-performance-with-ssr-and-rust-part-i-ssr-4n1p) 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](https://www.rust-lang.org)). In addition, `Actix-web` framework is the fastest one according [TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r18).

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](https://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:

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

Now we can add our dependencies:

```toml
[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"
```

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

```rust
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();
```

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

We are ready for actually Web Server code now:

```rust
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
```

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](https://dev.to/pagespeed-green/how-to-improve-react-app-performance-with-ssr-and-rust-part-i-ssr-4n1p)). 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:

```rust
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
        }
    }
}
```

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](https://github.com/pagespeed-green/rust-ssr-webserver).

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](https://pagespeed.green) website.

Happy coding!

---

* [Website vector created by slidesgo - www.freepik.com](https://www.freepik.com/free-photos-vectors/website)