1. 18
    Expose Specific Environmental Variables to The Client Using Remix
    4m 56s

Expose Specific Environmental Variables to The Client Using Remix

Kent C. Dodds
InstructorKent C. Dodds

Share this video with your friends

Send Tweet
Published 3 months ago
Updated 3 months ago

We want to make it so that only admins can access the admin routes. To do that, we need to add support for environment variables. Using Remix, we can choose which environment variables to expose to our front or backend.

Using an env.server file, we can create a function that returns an admin email address that we can use for accessing those admin routes. This will allow the front-end access to an env variable. You can still use regular env variables for server-side.

Kent C. Dodds: [0:00] We want to make it so that only admins can access the admin routes. To do that, we need to add support for environment variables. Our server runs on the server and so, it has access to stuff like console.log(process.env). We have access to all of the environment variables of our environment.

[0:20] We can't put that in our component because when our component is run, it's going to be run on the server where this will work. It's also run on the client where process.env does not exist. A lot of bundlers will just process this at build time, but Remix does not do this.

[0:38] We are going to expose specific environment variables to both our frontend and our backend in a quite simple way. What we're going to do is in the root of our app directory, we're going to create a env.server.ts file. Because this is a server file, this will have access to our process.env.

[0:58] Now, I'm going to make a function called getEnv(). This will return an object that has ADMIN_EMAIL, process.env.ADMIN_EMAIL. Then we'll export that function, and then we'll go into our entry server right here. We're going to say, "global.ENV = getEnv() from our env.server."

[1:22] That's going to get the env global variable accessible everywhere in the backend code. Then in our route, we're going to have our loader give us the env. We'll say, "getEnv." For now, we'll just say, "ENV is any." We'll fix that later.

[1:36] Then we'll add a script right here, that has a dangerouslySetInnerHTML, where the HTML is window.ENV = JSON.stringify(data.env). That data is going to come from useLoaderData. Now we're going to have an ENV variable that's accessible in all of our client-side code.

[2:03] We'll have an ENV variable that's accessible in all of our server-side code, and it will have exactly the environment variables that we want accessible both in the client and on the server. That's an important note.

[2:14] You will not want to put any environment variables in this object that you don't want exposed to the frontend. Potentially, ADMIN_EMAIL may not be something that you do want exposed to the front end.

[2:25] For our blog, it's not a big deal because we're literally going to put our admin email on our About page anyway, so that people can send us emails. It doesn't make a difference for us.

[2:34] Just to make sure that everything you put in here you are OK with getting to the client, because it's going to get there. If you have environment variables that you don't want to get to the client, that's what process.env is for. That will still be accessible on the server and only on the server.

[2:49] Now, if we save this and we go to our root file. In our loader, we can console.log(ENV) and then in our app, we can console.log(ENV). Then we come over here, we refresh this page, and let's not slow this down. No throttling there.

[3:09] Look at our console and here is that object. If we look at this console, you see the object there.

[3:15] Now, of course, we haven't set the ADMIN_EMAIL yet. While we're at it, let's just go ahead and go to our .env, we'll add ADMIN_EMAIL is rachel@remix.run. If we save that and restart our server, and reload the page, now, we've got our ADMIN_EMAIL in the client and in the server.

[3:35] The last thing that I want to do here is go to our env.server, and we're going to clean this up from a TypeScript perspective.

[3:44] Our ENV type is going to be the ReturnType of the typeof our getEnv function. As we add new variables to this, our ENV will always be correct. Now we see that the ADMIN_EMAIL can be a string or undefined.

[3:58] I don't ever want it to be undefined. What we're going to do is add an invariant here that says, "if the ADMIN_EMAIL is not defined then we can say 'ADMIN_EMAIL should be defined.'"

[4:11] Now, our type says that there is absolutely an ADMIN_EMAIL, and then we're going to declare on global a variable called ENV. That is that ENV type. On the interface window, we'll say, it has a variable called ENV, which is the ENV type.

[4:29] Now, if we go to our entry server, our global ENV is happy about this and if we go to our root, our console.log(ENV) is happy about this. In fact, we can even do ENV.ADMIN_EMAIL and that gets auto-completed for us.

[4:45] Then, finally, we can have this ENV be typed correctly to be the ReturnType of typeof getEnv. Then, we can delete our console.log right here and we're all set.