trunk
trunk copied to clipboard
process.env vars
is there a way to set and make these accessible via trunk serve ?
Prior art from NextJS:
In order to make the value of an environment variable accessible in the browser, Next.js can "inline" a value, at build time, into the js bundle that is delivered to the client
Doesn't plain env! from Rust work for that?
I don't think so, wasm in the browser can only access whatever the JS bindings can, and there's no environment to speak of really. It's not uncommon for bundlers to pass variables into a process.env object so that JS code can work the same as the browser and NodeJS environments. I have a little helper to extract it this way: https://docs.rs/awsm_web/latest/src/awsm_web/env.rs.html#10-18
No strong opinion from me about how Trunk might want to go about this... prior art isn't necessarily the only way to do it :)
for now I'm accomplishing the same thing via feature-gating, but, it would be nicer to be able to avoid that :)
This works during compile time: https://doc.rust-lang.org/std/macro.env.html
So when you use:
fn my_foo() -> String {
env!("MY_FOO").into()
}
And compile it with:
env MY_FOO=bar cargo build
Then my_foo() would return bar.
But that's a plain Rust functionality. I don't think Trunk interferes with that.
ah, thanks for pointing that out - yeah I was thinking of env::var for runtime
it's hard to think of a very compelling use-case for needing to access process.env at runtime specifically, closing this issue for now. Thanks!
in case anyone else lands here, just a tip -.env isn't loaded out of the box. Instead of having a separate trunk hook, you can load the .env as a cargo build script
Cargo.toml
[build-dependencies]
dotenvy = "0.15.7"
build.rs (next to Cargo.toml, not in src)
use dotenvy::from_filename_iter;
fn main() {
// Attempt to read the .env file
if let Ok(iter) = from_filename_iter(".env") {
for item in iter {
match item {
Ok((key, value)) => {
// Set the environment variable for the compiler
println!("cargo:rustc-env={}={}", key, value);
}
Err(err) => {
// Handle parsing errors (e.g., invalid lines in .env)
eprintln!("Warning: Failed to parse .env entry: {}", err);
}
}
}
} else {
// .env file is missing; this is not an error
println!("cargo:warning=.env file not found. Skipping environment variable loading.");
}
}
@ctron after using this for a while, I think this is a worthwhile feature request, so re-opening for now.
The real-world problem is when I do nothing other than change the .env and re-run trunk serve, I run into cache problems. The answer to this, as far as I can tell, gets even nastier than the already-not-ideal stuff above: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-env-changed
Altogether it would be MUCH nicer to not have to worry about any of this. As a user, I'd want Trunk to deal with injecting env vars, application code can just read it at runtime (yes by reaching out into JS like above) - that way I can just start/stop trunk and get the new vars without rebuilding any of the Rust stuff
As noted at the top of the issue, this is available in other frameworks such as NextJS, I think it's genuinely a lacking feature at the moment.
Hm. I am still not sure I understand what trunk should exactly do here? Re-reading the .env file during runtime?
My ideal would be for it to inject into a JS object (e.g. process.env). I think that's when Next/Vercel does.
Both serve and build would do this- effectively if you edit something in your environment vars, and restart the dev server (or rebuild the final bundle), you see the new vars, even if the Rust code didn't change/rebuild at all
For the sake of not accidentally exposing sensitive vars, the actual vars can be allowlisted in Trunk.toml, rather than just read from all env vars
In terms of getting from a .env file into the environment- yes, it would also be nice for Trunk to do this step, but they are two separate things (e.g. env vars could also be populated via CI or something)
Ok, just summing this up, the idea is to:
- Have trunk export the env-vars (possibly filtered) into a JSON struct
- Having that JSON struct assigned in the
index.html(e.g. toprocess.env) - Refresh that struct when doing
trunk watch(orserve).
yes, with the addition that it would be nice to point Trunk to a .env too.
this isn't as essential, since one can use a shell script or task runner of some sort to do that part - but it's nice to keep everything in Trunk
would be good to get more feedback on this issue, but I think it's a fairly common need
Could you come up with a PR for this?
sure, I'll put it on my local TODO list for myself, but tbh it's going to be a while until I get to it, most likely
I'm not sure if this was ever implemented, but I just wanted to share my thoughts on how I would prefer to use .env files with Trunk, and why the above approach might not be ideal.
In tools like Vite (and others), there's a convention where only environment variables with a specific prefix—like VITE_—are exposed to the client. For example, with a .env file like this:
VITE_AUTH_ENDPOINT=https://example.com
SLACK_API_TOKEN=xyz
only VITE_AUTH_ENDPOINT would be included in the final bundle and accessible to the end user. This is really helpful when you have environment variables that are needed during the build process but shouldn't be exposed publicly, or when other tools also rely on the same .env file.
I may have misunderstood the intent, but if the implementation just exposes the entire .env file, that could catch some users off guard—especially if they have secrets or credentials in those files.
I hope this perspective is helpful!
oh, I completely forgot about this... not on my personal radar at the moment. Imho an allowlist in Trunk.toml is a bit better than compiling in everything with a TRUNK_ prefix, since it prevents accidental contamination and also allows using global env vars without having to rename them