DEV Community

Cover image for How to pass environment variables to a Rust WASM application like Yew, Dioxus and Leptos as a TypeScript Developer
Jose
Jose

Posted on

How to pass environment variables to a Rust WASM application like Yew, Dioxus and Leptos as a TypeScript Developer

Hello TypeStaceans!

Here's a step by step process in how you can pass environment variables to a Rust WASM application framework using Yew, Dioxus, Leptos, Sycamore, or your favorite one.

GitHub Repo
TypeStacean - Learn Rust WASM from TypeScript

How you do it in TypeScript

Given a TypeScript React, Vue, Svelte, or Vanilla TypeScript app, you'd generally have an .env at the root level of your project:



APP_S3_URL="https://aws.amazon.com"
APP_AUTH0_ID="asdk211jrifenf"
APP_OTHER_NON_SENSIBLE_INFO="h123213"


Enter fullscreen mode Exit fullscreen mode

And then, you could either augment it in a .d.ts file or create an object and expose it.



declare global {
  namespace NodeJS {
    interface ProcessEnv {
      APP_S3_URL: string;
      APP_AUTH0_ID: string;
      APP_OTHER_NON_SENSIBLE_INFO: string;
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

This .env would then be injected at build time.

It isn't possible to pass it as runtime (unless you fetch it from an external resource) as you need to run a process for it do so.

In Web Assembly, things are the same.

Environment Variables in Rust Web Assembly (Yew, Dioxus, Leptos).

In WebAssembly we need to pass the environment variable at compile time.

Therefore we cannot use the dotenv crate (similar to npm's dotenv) as it's only available in runtime.

We will leverage Rust's built-in build.rs script system to read the environment at build time and then generate a custom .rs file which we can read afterwards.

Never store server-sensitive information in your front-end .env files as these are accessible to the public

Using build.rs to inject the environment variables.

  1. Go to the root of your project and create an empty build.rs file and an empty .env file.

Root Directory

  1. Add dotenv to the build-dependencies part of Cargo.Toml ```toml

[package]
name = "typestacean-learn-rust-wasm-from-typescript"
version = "0.1.0"
edition = "2021"

Add these:

[build-dependencies]
dotenv = "0.15.0"

This will enable dotenv to be used in `build.rs`

3. Fill in the build.rs script
```rust


// build.rs
use dotenv::dotenv;
use std::env;
use std::fs::File;
use std::io::Write;

fn main() {
    println!("cargo:rerun-if-changed=.env");
    let dest_path = "./src/env.rs";
    let mut f = File::create(&dest_path).unwrap();

    // use the dotenv crate to get the .env values
    dotenv().ok();
    f.write_all(b"// This file is automatically generated by build.rs\n\n")
        .unwrap()
    for (key, value) in env::vars() {
        if key.starts_with("APP_") {
            let line = format!(
                "pub const {}: &'static str = \"{}\";\n",
                key,
                value.replace("\"", "\\\"")
            );
            f.write_all(line.as_bytes()).unwrap();
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

Here's the breakdown:

  • We define a main() function that will be used as the entry point
  • The println!("cargo:rerun-if-changed=.env"); tells cargo to prevent running the build.rs script if the .env file hasn't changed.
  • We then set to generate env.rs in src/env.rs
  • We then call dotenv().ok() to load the environment from the .env to the env::vars().
  • The b"" means that the strings should be represented as a sequence of bytes.
  • We iterate the environment line per line, we process it so we can generate something like: ```

pub const APP_WASM_FRAMEWORK: &'static str = "leptos";

* This is then written in the env.rs file

Include the .env.rs file in your script



// Other imports omitted
mod env;

fn main() {
   println!("{}", env::APP_WASM_FRAMEWORK);
}



</code></pre></div><h2>
  <a name="feedback-appreciated" href="#feedback-appreciated">
  </a>
  Feedback appreciated
</h2>
<h2>
  <a name="follow-to-learn-rust-wasm-as-an-experienced-typescript-developer" href="#follow-to-learn-rust-wasm-as-an-experienced-typescript-developer">
  </a>
  Follow to learn Rust WASM as an experienced TypeScript developer
</h2>

<p>I'm currently learning and sharing my knowledge with Rust's most popular WASM frameworks: Yew, Dioxus, Leptos.</p>

<p>I want to help TypeStaceans (TypeScript developers who want to learn Rust Web Assembly) to grasp Rust WASM as fast as possible by sharing the distilled knowledge.</p>

<p>Follow me on:</p>

<ul>
<li>dev.to - <a href="https://dev.to/javiasilis">https://dev.to/javiasilis</a>
</li>
<li>twitter - <a href="https://twitter.com/javiasilis">https://twitter.com/javiasilis</a>
</li>
<li>linkedin - <a href="https://linkedin.com/in/javiasilis">https://linkedin.com/in/javiasilis</a>
</li>
</ul>
Enter fullscreen mode Exit fullscreen mode
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
gekh profile image
German Khokhlov

Easier way is to use inline env vars:

API_URL="http://127.0.0.1:9000" dx serve --port 8008
Enter fullscreen mode Exit fullscreen mode

and in code:

let url = env!("API_URL");
Enter fullscreen mode Exit fullscreen mode

docs: doc.rust-lang.org/std/macro.env.html

Collapse
 
askrodney profile image
Rodney Lab

Thanks for putting this together Jose, it was super useful.