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?
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
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
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
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 usingdot-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 thenew DOMParser().parseFromString
in the client, which is available in the browser and essentially free.
Hi @juanma,
Thank you so much for putting in all of this time and energy to answer my question! I really appreciate it
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
This helps greatly!! Thank you again for putting in the time to explain this to me.