How to set up environment varying code in your Rust web app
There are times when you’ll want your app to run different code depending on which environment it’s in: development, production, etc.
For example: You provide your templating engine with a baseurl
variable. In production, you want it to point to https://my-app.com
, but in development you want it to point to http://localhost:8888
.
We can accomplish this in Rust-y web apps with environment variables and the dotenv crate, as demonstrated in the following post.
Create a .env
file
We’ll start by creating a .env
file at the top level of our local project and inserting the following variable. If you already have a .env
file, then just insert it on a new line.
PRODUCTION=
We’ll use that variable to provide a bool
stating whether or not the app is running in production. Locally, there shouldn’t be any characters after the =
sign. In your production server’s .env
, insert the following:
PRODUCTION=1
Add the dotenv crate
Now that we have .env
files with the appropriately configured variable in our various environments, we can use them in our app.
We’re going to be using the popular dotenv crate to make the variables available to our app. Let’s add it to our Cargo.toml
…
[dependencies]
dotenv = "0.10.0"
// […]
…and import it:
extern crate dotenv;
// …
use dotenv::dotenv;
// …
Lastly we’ll tell dotenv to load the variables contained in the project’s .env
into our app. Place the following right at the top of fn main
:
dotenv().ok();
Using the variables
With that done, we can access the environment variables in the file the same way we would access the system’s variables.
Start by importing the standard environment library:
use std::env;
There are a few ways to approach the next part. If you’re going to be using this across different files and functions in your app (likely), then creating a lazy_static
is probably the way to go. For brevity, I’m going to assume you’re only using the PRODUCTION
variable within one function though.
Add the following to the function in question:
let is_in_production = env::var("PRODUCTION").is_ok();
env::var
returns a result based on (a) if the variable is present and (b) if it is valid unicode. The variable with nothing after the =
sign we created above was purposefully mal-formatted so that when we call is_ok
on the result of env::var
, it returns false
because env::var
returns an error. In production, as the variable is correctly formatted in that environment, is_ok
will return true
.
Now in our function we can do:
if is_in_production {
// Our app is running in production…
} else {
// Our app isn't running in production…
}
Conclusion
This approach can be extended for code that varies again if running in staging or testing or both. Just set, for example, STAGING=1
where appropriate and add an else if
branch to the existing conditional(s):
let is_in_production = env::var("PRODUCTION").is_ok();
let is_in_staging = env::var("STAGING").is_ok();
if is_in_production {
// Our app is running in production…
} else if is_in_staging {
// Our app is running in staging…
} else {
// Our app isn't running in production or staging…
}