How to read Environment Variables

Hello!
I am trying to use a .env file in order to define environment variables (e.g. API Keys) so that my sensitive information is not exposed client side. However, I cannot utilize the dotenv npm package that I usually use (https://www.npmjs.com/package/dotenv) since there is no “server” file. Can you please help?

1 Like

Hi @jeanettepranin,

What Frontity is providing is a framework to ease the connection between a React app (Frontend) & Wordpress Headless (Backend). Any extra settings you need to do in the backend can be done directly in Wordpress (custom code, plugins, etc…)

Besides this, you can also have additional servers, serving different data to be consumed by your React app.

To use an additional source of data for your Frontity project while keeping API credentials private, you must create an additional web server (NodeJs + Express for example) to act a “proxy” and there you can install any npm package you need (dotenv for example) and use any environment variable you may need

Hope this helps

If you have any more questions let us know :slight_smile:

PS: If you can you provide a repository URL we’ll be able to help you better with your project/theme

Hi Juan,

Thanks for your quick reply! Sadly I cannot share my repo as it is private, but could you link me to an example? I’m not sure how to connect another server to my frontity app.

Would it just be a matter of running multiple servers, like Option 1 in https://itnext.io/4-solutions-to-run-multiple-node-js-or-npm-commands-simultaneously-9edaa6215a93?

Hi @jeanettepranin,

I’ve been talking to the developers and It seems you can actually pass environment variables to the Node server in Frontity.

Let me gather all the information and prepare a little demo and I’ll be back to you ASAP

1 Like

Thank you, Juan!! That will be super helpful to see.

Will the environment variables be masked / secure?

Hi @jeanettepranin,

Regarding your question, a few considerations…

Isomorphic React apps in Frontity

When developing a Frontity project or package the React code (used in your custom theme) should be isomorphic (also called universal)

This means that all the code in a Frontity project should be prepared to be executed both on the server-side and in the client-side.

Every time we access a page the first load is rendered from the server and from there the navigation is done in the client-side (this allows a SEO friendly behavior while maintaining a good UX)

Let’s take an example of an isomorphic react app:

If we enter the URL of the pageA and press Enter, then pageA is rendered in the server and served to the client
If we enter the URL of the pageB and press Enter then pageB is rendered in the server and served to the client

In these two cases, a SSR (Server Side Render) process has taken the React code and created the proper HTML with the proper content that is “served” to the client so it can be displayed to the user

But if we enter the URL of the pageA, press Enter and from any some link in our app we go to pageB what is happening is:

  • pageA was rendered in the server and served to the client
  • pageB was rendered in the client and displayed to the user

So, as you can see, we have to keep in mind this when developing a React theme with Frontity

Open drawing

Luckily, ALL the tools provided by Frontity provide an isomorphic behavior (they assure a behavior that works in both the server-side & the client-side)

So taking this into account…

Creating different entry points

We can actually create 2 different entry points for our React theme in Frontity → instead of having and index.js we can split it in:

  • client.js → the entry point of our app when client-side takes controls
  • server.js → the entry point of our app when server-side takes controls

If Frontity finds those files, it will import the server.js one in Node and the client.js one in the browser, and it will ignore the index.js file. It can still exist, though.

In the server.js you can define a piece of code that will be executed BEFORE this SSR process is executed where you can actually do what you asked, this is, using an environment variable that is not visible for the client

Adding environment variables to a Frontity Project

To do that, you can use packages like cross-env or dot-env. Whatever approach you prefer.

If you use cross-env, you don’t have to do anything special in Frontity, just add it to your package.json scripts:

{
  "scripts": {
    "dev": "cross-env MY_VARIABLE=xxx frontity dev",
    "serve": "cross-env MY_VARIABLE=xxx frontity serve",
    "build": "cross-env MY_VARIABLE=xxx frontity build"
  }
}

dotenv can only run in Node, so you must divide your index.js file in two files: client.js and server.js.

More info about the different entry points in the docs: https://docs.frontity.org/learning-frontity/packages#entry-points.

So for dotenv create an .env file:

MY_VARIABLE=xxx

Accessing those environment variables privately (only server) in a Frontity Project

Here you have a little demo of how to use an environment variable to perform a request to an external API and storing this data in the state so it can be accessed from your React components

The server.js looks like this…

import { config } from "dotenv";
import { fetch } from "frontity";
import packageClient from "./client";

// Launch dotenv.
config();

export default {
  ...packageClient,
  actions: {
    theme: {
      ...packageClient.actions.theme,
      beforeSSR: async ({ state }) => {
        const {API_TMDB} = process.env
        const URL = `https://api.themoviedb.org/3/movie/550?api_key=${API_TMDB}`
        const detailsMovie = await fetch(URL)
          .then( response => response.json() )
        state.tmdb = { detailsMovie }
      }
    }
  },
};

In this example, there’s a API_TMDB environment variable defined in a .env of that project

This method (beforeSSR defined in the server.js) will assure that your API’s are secure (will not be part of the client bundle) and only visible from the server-side (but this logic will be executed for any page loaded the first time)

Accessing environment variables (client & server) in a Frontity Project

If you need to use the ENV variable also in the client, the best way is to add it to the state.

You can use frontity.settings.js or your package state for that, whatever is more appropriate for your situation.

frontity.settings.js:

import { config } from "dotenv";

// Launch dot-env.
config();

const settings = {
  name: "my-project",
  state: {
    env: {
      myVariable: process.env.MY_VARIABLE
    }
  },
  packages: [
    // ...
  ];
}

packages/my-package/src/server.js:

import { config } from "dotenv";

// Launch dot-env.
config();

export default {
  state: {
    theme: {
      myVariable: process.env.MY_VARIABLE,
    },
  },
};

Either way, the ENV variable will be serialized with the rest of the state and it will be sent to the client for the React hydration.

Please note that any ENV variable exposed in state will end up in the client. Do not expose any secret API KEY or password.

One last consideration

Using beforeSSR is independent of using a server.js file. You can add a beforeSSR function to your index.js and it works fine. The code, of course, will make it into the client bundle, but the action won’t be called there (unless you call it manually).

The only moment where you may want to divide between two separate client.js and server.js files is:

  • If you need to access Node libraries, like "fs" or "path" , because that will fail if it’s present in the client bundle. For example, when using dot-env.
  • If the code contains something that cannot be exposed to the client. For example, a hardcoded API key.
  • If you are using a heavy library on the server that will increase the size of the client bundle unnecessarily. For example, we use he to decode entities in the server, but it weights 73Kbs so we use the new DOMParser().parseFromString in the client, which is available in the browser and essentially free.
2 Likes

Hi @juanma,
Thank you so much for putting in all of this time and energy to answer my question! I really appreciate it :blush:

I have one more quick question. If I want to make a call to an external API using a secret API key when a user clicks on a button (for example, if a user enters their email and clicks Submit, add them to my private email list serv), how would I do this?

Normally, what I would do is create a REST endpoint in my server code (for example, localhost:XXXX/my-custom-endpoint) that, when called, would execute this function for me without ever exposing the API key to the client. However, the concept of HTTP Post endpoints in Frontity is not immediately clear to me - I cannot find this type of functionality described in the documentation.

If you can explain it that would be amazing! If this has been explained elsewhere, please let me know where.

Hi @jeanettepranin,

To do requests to an external API while keeping the API key private you have several options:

No custom endpoints

From Frontity

Using the beforeSSR as I explained in my previous response → this allows you to perform a request to an external API before any page is served from the server

In the future, we will add server extensibility. It’ll be something like this: Express middleware

Custom endpoints

From Wordpress

If you need several endpoints and you don’t want to use an extra server you can also prepare some custom endpoints in your Wordpress → https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/

From a serverless function

Another quick (and elegant) way of preparing some custom endpoints is using a serverless function. We recommend you to use Now → https://zeit.co/docs/v2/serverless-functions/introduction

From another webserver

And of course, the solution you suggested

Normally, what I would do is create a REST endpoint in my server code (for example, localhost:XXXX/my-custom-endpoint) that, when called, would execute this function for me without ever exposing the API key to the client.

You can prepare an extra server (w/ Node & Express for example) with all the custom endpoints you need


As you want to do these requests as a result of triggering an event, these custom endpoints should be called from the client-side of your React app

Hope this helps

1 Like

This helps greatly!! Thank you again for putting in the time to explain this to me.

1 Like