Hey!
Quick question from my side, perhaps I am doing something wrong here.
I have an env var called PAYLOAD_PUBLIC_FRONTEND_URL
. I use it in one of my collections for the preview functionality:
preview: (doc, { locale }) => {
if (doc?.slug) {
payload.logger.info(`${process.env.PAYLOAD_PUBLIC_FRONTEND_URL}/${locale}/post/${doc.slug}`)
return `${process.env.PAYLOAD_PUBLIC_FRONTEND_URL}/${locale}/post/${doc.slug}`;
}
return null;
},
When I run my app locally, environment variable gets propagated, but on PRODUCTION it is undefined. What is even weirder is that other environment vars I pass to my docker container work. What is yet even weirder is that the same variable in payload.init() gets logged with the actual value.
payload
.init({
secret: process.env.CMS_SECRET,
mongoURL: process.env.MONGODB_URI,
express: server,
onInit: async () => {
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
payload.logger.info(`Frontend server URL: ${process.env.PAYLOAD_PUBLIC_FRONTEND_URL}`);
Is there something that might be preventing collections to access process, but not payload.init
? I feel I might be missing something here
Thanks for any kind of feedback!
Hey @adam-mrozik — I have some thoughts for you.
Are you using dotenv
? Make sure that your dotenv
correctly points to your .env
file in both dev and production. Often times we've seen it where, for whatever reason, the folder structure differs from /src
to /dist
if using TS, and then dotenv
can no longer find your .env
file.
dotenv
unfortunately won't error if it can't find the .env
....It just simply won't bind any variables.
You can circumvent this problem by specifying a path
to dotenv
that you are SURE points to your .env
file in production cases.
Hey @jmikrut , thanks for the answer!
To be honest, I thought having them just as environment variables would be sufficient. I do not copy any .env
file into production at all, I just have them all as bash variables. And it works perfectly in server.ts
When I provide this variable as ENV during docker build, it works properly as well. So I assume .env
is an alternative, not something required
I even created one file env.ts in root that just reads all the variables:
const ENV = {
PAYLOAD_PUBLIC_FRONTEND_URL: process.env.PAYLOAD_PUBLIC_FRONTEND_URL, ...}
export default ENV;
And, everything gets propagated from this file, Secret, mongoDB, etc... even when I went into the container, manually edited this file to log console.log(ENV.PAYLOAD_PUBLIC_FRONTEND_URL)
and did node env.js
on that file, it showed my URL. But for some reason, it fails when used inside a preview function.
Variables are present in the env of my running container:
Again, the only thing that helps is if I add this variable as an env during the build time. Cannot get my head around what is happenning with that one in particular
OK so here's what's happening.
In the context of the server, environment variables are actual real-life variables that can be read from memory.
But the server's memory is in no way shared with the browser. So when you are using process.env.XXX
in the browser, that is a totally different "construct" that is generally built out as a global variable for you just for reasons of consistency.
It's done by Webpack. And Webpack literally writes your env variables into your browser JS bundle on build. That's why we specify that any public variables (aka safe for anyone to read) should be strongly prefixed, clearly, with PAYLOAD_PUBLIC_
.
But, with a full understanding of the above, you probably now see that those variables must be present while your bundle builds, because Webpack needs to write them directly into the JS bundle that it produces. This goes the same for any bundling framework - not just Webpack. So if you don't have your environment variable present while building Payload, then it will not be present in the browser.
Does that make sense?
Yeah, makes sense! Thanks for the explanation!
Okay this caught me off guard I have to admit. Not that I haven't dealt with this kind of stuff before (in fact I remember fixing this exact problem at a previous workplace) but I guess it just surprised me that Payload hasn't implemented dynamic environment injection at runtime. Not having this makes configuration a bit of a pain in the sense that the Docker image isn't actually portable in the end when it's built. I might actually want to take a stab at fixing this I think if that's something that more than just me want. @jmikrut what do you think?
Addition:
It works correctly if I supply this variable during docker-build. But this seems widely inconsistent, as it works in server.ts without it
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.