Environment variables in Expo projects
.env
is for environment specific variables (DEBUG=true), it's not for storing secrets (API keys..)- The
process.env
property returns an object containing the user environment. Check out all the environment variables that are set
console.log(process.env);
- You can't
process.env
use values inapp.json
, but you can use them inapp.config.js
. So, for example,hooks.postPublish.config.authToken
for Sentry was inapp.json
before (as recommended in the docs), but if you useapp.config.js
you can load that auth token in via a secured environment variable. - On Windows, we use
set
to define environment variables while on Linux we useexport
- In React Native,
__DEV__
is a global variable with a boolean value that indicates whether you're running in development mode or not. iOS or Android simulators will have his valuetrue
__DEV__
can not be set insidebabel.config.js
(Expo). You'll get
node_modules/expo/AppEntry.js: __DEV__ is not defined
Failed building JavaScript bundle.
Expo options:
babel-plugin-transform-inline-environment-variables
will replace environment variables values inside your code withprocess.env.BLAH
, but you need to pass aninclude
array while configuring it in Babel in order for any variable to work. ref. Will NOT work with a.env
file
- How to load variable values from a file
.env
- How to reference those values in config file
app.json
,babel.config.js
etc.
For production
to be available in code, you need to pass it when starting expo. Start the app/server in production
mode
# setting env vars
NODE_ENV=production expo start
# require dotenv when running the script
node -r dotenv/config dotenv-example.js
NODE_ENV
and BABEL_ENV
are both undefined by default\
Expo - Release channels
You can use release channels --release-channel
to set environment specific variable configuration. To set a release channel, you pass it when you run the command.
expo publish --release-channel prod-v1
And you can reference it with Constants.manifest.releaseChannel
import { Constants } from 'expo'
console.log(Constants.manifest.releaseChannel)
dotenv
- loads values from
.env
file - config can be passed with
-r
when running the script - does not override existing env vars. for example, you can't set
NODE_ENV
inside your.env
dotenv vs. release channels
- Release channels use
Constants.manifest.releaseChannel
to change things inside the code, while dotenv usesprocess.env
. Both need some value to be passed while running the command. Neither can be used for secrets.. - Default values for
releaseChannel
areundefined
in dev mode, anddefault
in production
const getEnvVars = (env = Constants.manifest.releaseChannel) => {
// Default values for `releaseChannel` are `undefined` in dev mode and `default` in production
if (__DEV__) {
return ENV.develop
}
// using `indexOf` will let you pick up dev, develop, development, dev-v1, dev-v2, dev-v3, and so on..
// Returns `-1` if the value is not found.
if (env.indexOf('dev') !== -1) return ENV.develop
if (env.indexOf('staging') !== -1) return ENV.staging
if (env.indexOf('prod') !== -1) return ENV.production
// If you do not specify a channel, you will publish to the `default` channel.
return ENV.develop
}
export default getEnvVars()
react-native-dotenv
react-native-dotenv if you don't wanna deal with any native code integration. It can be used with Expo as well
- Create a
.env
file npm i react-native-dotenv
- Update
babel.config.js
to include the preset"module:react-native-dotenv"
- may need
'module:metro-react-native-babel-preset'
as well if an RN projet. Not sure about Expo as expo has'babel-preset-expo'
module.exports = {
presets: ['module:metro-react-native-babel-preset', 'module:react-native-dotenv'],
}
- Now you can use it in JS files
import { API_KEY, ANOTHER_CONFIG } from 'react-native-dotenv'
ApiClient.init(API_KEY, ANOTHER_CONFIG)
Caveeat: ONLY supports development
.env
and production.env.production
environment configs. Which kind of makes sense when you take into account that Xcode only has Release and Debug modes, and Android requires you to unistall the debug version in order to install the release version. reftl;dr: can't use sine i have
staging
as well
Secrets!
Not in the code, never. Don't commit to git. Nope.
Anything included in your code could be accessed in plain text by anyone inspecting the app bundle
The best place i could think of is as environment variables inside Github Actions or Bitbucket Pipelines. That way you can set them securely, manage who has access, and pass them to your build process.
Bitbucket has 3 different levels of environment variables
- Team and individual account variables
- Repository variables
- Deployment variables
and they can be secured, meaning you don't actually see the value after the variable has been set. Not in the settings, and not in the logs for the pipeline.
Links
- Environment variables in Expo
- https://www.twilio.com/blog/working-with-environment-variables-in-node-js-html
- Node: process.env
- Working with Environment Variables in Node.js
- Dotenv tutorial
- React Native: Security
- dotenv
- per-env
- Using Release Channels for Environment Variable Configuration
- How to gracefully use Environment Variables in a React Native app
- react-native-dotenv
- How to Run App in Production Mode | How to Debug React Native Apps in Development and Production
- StackOverflow: Encrypting sensivite data in React Native and Expo
- Node.js Everywhere with Environment Variables!