Reading Env Variables with Reason and/or Reason-Scripts


#1

I have done this a hundred time but i’m missing something here.

Quick demonstration repo with react-scripts.

In Demo.re Im logging values from .env using @ahrefs/bs-dotenv.

running: node ./lib/js/src/Demo.js | grep auth0 returns

AUTH0_DOMAIN: 'tenant.eu.auth0test.com',
  AUTH0_CLIENTID: 'DrandomstringxHauth0test' }
domain tenant.eu.auth0test.com
clientID DrandomstringxHauth0test
From Demo.re { domain: 'tenant.eu.auth0test.com',
  clientID: 'DrandomstringxHauth0test',

In app.re, logging the same data:

//app.re
...
Demo.authOptions |> Js.log2("Auth from App.re: ", _);

returns undefined for everything.

```bsh
ENV
Object {  }
bundle.js:44303:1
domain undefined
bundle.js:44307:1
clientID undefined
bundle.js:44311:1
From Demo.re
{…}
​
clientID: undefined
​
domain: undefined
​
redirectUri: "http://localhost:3000/callback"
​
responseType: "token id_token"
​
scope: "openid"
​
__proto__: Object { … }
bundle.js:44321:1
Auth from App.re:  
{…}
​
clientID: undefined
​
domain: undefined
​
redirectUri: "http://localhost:3000/callback"
​
responseType: "token id_token"
​
scope: "openid"
​
__proto__: Object { … }

Decifering Type Error message
#2

You don’t need any additional packages to use .env files in create-react-app, it already has this feature out of the box. But you need to follow some conventions for the vars names https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables


#3

Also I think they should keep the form process.env.REACT_APP_AUTH0_DOMAIN so maybe using bs.val to bind to the global value is the best way. It’s how I do

[@bs.val] external foo : string = "process.env.REACT_APP_FOO";

#4

Thanks, @gabrielrabreu for that point to the cra docs. I had not looked at that in a while.

So check this out. The example you gave did not work for me unless you pass it to where you are using it like so:

//Demo.re
[@bs.val] external foo : string = "process.env.AUTH0_DOMAIN";

[@bs.val] external foo2 : string = "process.env.AUTH0_CLIENTID";

[@bs.val] external foo3 : string = "process.env.FOO3";

[@bs.val] external foo4 : string = "process.env.FOO4";

Dotenv.config(); // doesnt work without Dotenv

type authOptions = {
  .
  "domain": string,
  "clientID": string,
  "redirectUri": string,
  "responseType": string,
  "scope": string,
};

let authOptions = {
  "domain": foo,
  "clientID": foo2,
  "redirectUri": "http://localhost:3000/callback",
  "responseType": foo4,
  "scope": foo3,
};

Js.log2("From Demo.re", authOptions);

which gets you:

{ domain: 'tenant.eu.auth0test.com',
  clientID: 'DrandomstringxHauth0test',
  redirectUri: 'http://localhost:3000/callback',
  responseType: 'foo4value',
  scope: 'foo3value' }

but calling Js.log(Demo.authOptions); from app.re gets you undefined:

clientID: undefined
​
domain: undefined
​
redirectUri: "http://localhost:3000/callback"
​
responseType: undefined
​
scope: undefined

Whats up with env vars in Reason? Or what am I doing wrong?


#5

Alright in then end i’m confused about why I can run in App.re

Demo.authOptions |> Js.log; and return:

Object { domain: "tenant.eu.auth0test.com", clientID: "DrandomstringxHauth0test" }

but running node lib/src/js/demo.js on:

type authOptions = {
  .
  "domain": string,
  "clientID": string,
  "redirectUri": string,
  "responseType": string,
  "scope": string,
};

let authOptions = {
  "domain": EnvConfig.react_domain,
  "clientID": EnvConfig.react_app_id,
};

authOptions |> Js.log;

from Demo.re with running node /lib/js/src/demo.js returns

{ domain: undefined, clientID: undefined }

Both functions are calling the same authOptions record so I’m guessing this has to do with create-react-app’s webpack configuration?


#6

Yeah I think you only get indeed the values when running the code via webpack so it will replace properly the envs


#7

If you are using reason-scripts with create-react-app you can use env variables like so:

[@bs.val]
external graphcoolDev : string = "process.env.REACT_APP_GRAPHCOOL_LOCAL";

[@bs.val]
external graphcoolProd : string = "process.env.REACT_APP_GRAPHCOOL_DEV";

[@bs.val] external env : string = "process.env.REACT_APP_ENV";

let uri = env === "dev" ? graphcoolDev : graphcoolProd;

In your .env you would have this:

REACT_APP_GRAPHCOOL_LOCAL=some_url
REACT_APP_GRAPHCOOL_DEV=some_url

For the REACT_APP_ENV I am running it from the command line. My start command looks like this:

"start": "REACT_APP_ENV=dev react-scripts start",


#8

I use this approach:

// Env.re
[@bs.val] external port : Js.Nullable.t(string) = "process.env.PORT";

// Index.re
let port =
  switch (Js.Nullable.toOption(Env.port)) {
  | Some(port) => int_of_string(port)
  | None => 3000
  };

#9

See also When using bs.val how to deal with undefined?