Piecing Together a Rust Web Application
Tags: rust, webapp, beginners
Published: 2019-08-05 03:06:00

---
title: Piecing Together a Rust Web Application
published: true
tags: Rust,WebApp,beginners
canonical_url: https://rust.graystorm.com/2019/07/13/piecing-together-a-rust-web-application/
cover_image: https://rust.graystorm.com/wp-content/uploads/2019/06/plant-wood-old-rust-equipment-gear-1278587-pxhere.com_.jpg
series: Designing a Full-Featured WebApp with Rust 
---

![](https://i0.wp.com/rust.graystorm.com/wp-content/uploads/2019/07/leaf-petal-number-green-color-material-1110050-pxhere.com_.jpg?fit=840%2C560&ssl=1)<figcaption>Hrm… how to find the right piece on <a href="https://crates.io">Crates.io</a> ???</figcaption>

For over a decade, I’ve worked on web apps with Perl, the last several years with Catalyst/Moose/DBIC and a slew of internal abstractions. There are a bunch of features I expect to need in any web app: config files (at different platform levels); structured logging; database ORM; templating; cookie, authentication, and session controls; en/decryption for access secrets; etc. I spent most of the overnight hours playing with piecing together a Rust web application, though I still have much more to do. After struggling for hours for what amounts to 279 lines of Rust code, I decided it was well worth it. I’ll try to explore some of my problems and what I worked out. This might take more than one post, so I don’t put you to sleep.

## Global (Static) Application Config

This probably isn’t idiomatic Rust and frowned upon for even more reasons. I’m sure I’ll adapt to Rustic thinking as I learn, but for now, I’m liking this. I like to have a `Config struct` that is set up at init time and is immutable (music to the Rust compiler’s ears, I’m sure). I battled this with `const` and `lazy_static` and numerous other things. Eventually, I settled on a Crate that the author [seems to have stumbled into writing/publishing](https://internals.rust-lang.org/t/pre-rfc-lazy-static-move-to-std/7993/38) (unless I’m missing some context): [OnceCell](https://crates.io/crates/once_cell). Where I was having trouble getting `lazy_static` to work, `once_cell::sync::OnceCell` seemed to work for me rather quickly.

Coupled with that, I like the notion of having Config settings initialized by YAML or JSON or TOML files and also able to be overridden in some way – usually environment variables. This path (and an earlier [post](https://rust.graystorm.com/2019/06/28/config-issues/)) took me to the aptly named [Config](https://crates.io/crates/config) Crate. It does just what I need for pulling settings into a config from various places. I ended up adding the [dotenv](https://crates.io/crates/dotenv) Crate as well, because something else used it in an example. I’m not sure I’ll keep ALL of these options forever, but it’s in the mix for now. There are, obviously, many ways to allow overrides vs protect the settings on disk, and many ways to decide which platform you are running on besides an ENV variable – and I’m flexible.

EDIT: I just realized that putting `<Mutex>` on my type means I have to `lock()` it to read it – and that prevents other functions (and other threads) from reading it. Since I’m ok with CONFIG being immutable, I really don’t need the Mutex, so I dropped it.

My settings.rs module looks like this for now:

```
use config::{Config, Environment, File};
use dotenv::dotenv;
use once_cell::sync::Lazy;
use serde_derive::Deserialize;
use std::env;

#[derive(Debug, Deserialize)]
pub struct Server {
    pub run_level: String,
}

#[derive(Debug, Deserialize)]
pub struct WebService {
    pub bind_address: String,
    pub bind\_port: u16,
}

#[derive(Debug, Deserialize)] 
pub struct Settings {
    pub server: Server,
    pub webservice: WebService,
    pub database_url: String,
}

pub static CONFIG: Lazy<Settings> = Lazy::new(|| {
    dotenv().ok();
    let mut config = Config::default();
    let env = env::var("PPS\_RUN\_MODE")
        .unwrap\_or("development".into());
    config
        .merge(File::with_name("conf/default"))
        .unwrap()
        .merge(File::with_name(&format!("conf/{}", env))
            .required(false))
        .unwrap()
        .merge(File::with_name("conf/local")
            .required(false))
        .unwrap()
        .merge(Environment::with_prefix("PPS"))
        .unwrap();

    match config.try_into() {
        Ok(c) => c,
        Err(e) => 
            panic!("error parsing config files: {}", e),
    }
});
```

And config files like these examples:  
  
conf/default.toml:

```
[server] run_level = "default"
```

conf/development.toml:

```
[server]
run_level = "development"

[webservice]
bind_address = "0.0.0.0"
bind_port = 3000
```

conf/staging.toml:

```
[server]
run_level = "staging"

[webservice]
bind_address = "0.0.0.0"
bind_port = 3000
```

and, conf/production.toml:

```
[server]
run_level = "production"

[webservice]
bind_address = "0.0.0.0"
bind_port = 80
```

## Application Logging

Piecing together a Rust web application includes another area of big concern – logging! Logging can easily become a tremendous burden of bandwidth and storage space, but a single log record might explain a production incident and lead you to a quick fix! Structured logging is great for logging platforms because storing and especially searching can be greatly improved when the message is static and data fields attached to the log record fill in the variable gaps.

To get things going, I started with the Crates [log](https://crates.io/crates/log) and [simple_logger](https://crates.io/crates/simple_logger), but I probably will move to [slog](https://crates.io/crates/slog) for the structured logging. The very first line in my main() is to call setup_logging() so if anything breaks on app initialization, we should get a log for it. With CONFIG a global static, this simple function looks like this for now, but soon I will work out specifying the logging level in the settings so it can be verbose for devs, but tamer on production:

```
pub fn setup_logging() {
    simple_logger::init_with_level(log::Level::Info)
        .expect("Trouble starting simple_logger");

    let run_level = &CONFIG.server.run_level;
    warn!("Running as run_level {}", run_level);
}
```

We still have the web framework, the database, encryption and more to come. Next up, more single-word Crates: Iron, Rocket and Diesel. I’d love to hear what Rust developers think of this so far – suggestions are welcome. Here’s the [repository](https://github.com/jculverhouse/pinpoint_shooting) on Github – you can skip ahead and see what other messes I’ve made.

The post [Piecing Together a Rust Web Application](https://rust.graystorm.com/2019/07/13/piecing-together-a-rust-web-application/) appeared first on [Learning Rust](https://rust.graystorm.com).