React Native Expo Environment Variables - react-native

So I'm happy with the concept of environment variables as explained in this article and others
https://www.freecodecamp.org/news/how-to-gracefully-use-environment-variables-in-a-react-native-app/
Great, I've got my SOMETHING="something" stored so I can just use env.SOMETHING or whatever
The part I'm a little lost on is where you keep the live variables?
I would rather not do a solution like this as it seems you are still keeping your keys quite public and that you are just choosing based on the environment with if statements
Manage environment with expo react native
For example with an Express App deployment we have, we specify
let endPointURL = env.endPointURL
and then we keep a versoin of that variable locally and when it sits with AWS it is overridden by AWS servers as explained here
I was just wondering does something like that exist for Android and iOS builds (on the respective stores) or through Expo?
Thanks all

Honestly I think the way they go about it is a little silly. There may be a better way to go about than this, but I think I followed their documentation suggestions.
https://docs.expo.io/versions/latest/distribution/release-channels/#using-release-channels-for-environment-variable-configuration
They have a code snippet suggesting you create a function to look at the release configuration itself.
I interpreted it that you might do something like the code below and store your environment variables in a variables.js file and pull in your environment variables as such.
import Constants from 'expo-constants';
export const prodUrl = "https://someapp.herokuapp.com";
const ENV = {
dev: {
apiUrl: "http://localhost:3000"
},
staging: {
apiUrl: prodUrl
},
prod: {
apiUrl: prodUrl
}
};
function getEnvVars(env = "") {
if (env === null || env === undefined || env === "") return ENV.dev;
if (env.indexOf("dev") !== -1) return ENV.dev;
if (env.indexOf("staging") !== -1) return ENV.staging;
if (env.indexOf("prod") !== -1) return ENV.prod;
}
export default getEnvVars(Constants.manifest.releaseChannel);
Edit:
Now that Expo supports config file as app.config.js or app.config.ts, we can use the dotenv. Check this: https://docs.expo.io/guides/environment-variables/#using-a-dotenv-file

A simpler approach would be to export the env object instead of the function:
import Constants from 'expo-constants';
import { Platform } from "react-native";
const localhost =
Platform.OS === "ios" ? "localhost:8080" : "10.0.2.2:8080";
const ENV = {
dev: {
apiUrl: localhost,
amplitudeApiKey: null,
},
staging: {
apiUrl: "[your.staging.api.here]",
amplitudeApiKey: "[Enter your key here]",
// Add other keys you want here
},
prod: {
apiUrl: "[your.production.api.here]",
amplitudeApiKey: "[Enter your key here]",
// Add other keys you want here
}
};
const getEnvVars = (env = Constants.manifest.releaseChannel) => {
if (env === null || env === undefined || env === "" || env.indexOf("dev") !== -1) return ENV.dev;
if (env.indexOf("staging") !== -1) return ENV.staging;
if (env.indexOf("prod") !== -1) return ENV.prod;
}
const selectedENV = getEnvVars();
export default selectedENV;
// Import
import env from '..xxx/utility/env';

Get it in your ios-generated file, based on .env file:
In .env, write down GOOGLE_MAPS_API=abcde...
yarn add react-native-config
cd ios
pod install
In your Objective-C-compiled code, for example, AppDelegate.m:
#import "ReactNativeConfig.h"
NSString *mapsApiKey = [ReactNativeConfig envFor:#"GOOGLE_MAPS_API"];
[GMSServices provideAPIKey:mapsApiKey];
Credits to: ReactNative: Pass JS variable to AppDelegate based on https://github.com/luggit/react-native-config.
Android should work as well, but haven't tested / followed https://github.com/luggit/react-native-config.
Edit: required steps for Android:
<meta-data android:name="com.google.android.geo.API_KEY" android:value="#string/GOOGLE_MAPS_API"/> in AndroidManifest.xml.
In settings.gradle:
include ':react-native-config'
project(':react-native-config').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-config/android')
right after rootProject.name = 'XYZ'
In build.gradle:
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" right below import com.android.build.OutputFile and implementation project(':react-native-config') right below implementation "com.facebook.react:react-native:+" // From node_modules
Regarding "normal" usage in .ts, .tsx, .js files I'm declaring variables in .env based on https://github.com/goatandsheep/react-native-dotenv by declaring "module:react-native-dotenv" in babel.config.js in plugins, and it works like a charm like so:
import { ACCESS_TOKEN } from "#env";
...
headers: {
Authorization: `Bearer ${ACCESS_TOKEN}`,
Accept: "application/json",
},
Edit: important the eas build ignores .gitignore-declared variable, so if your .env is in .gitignore the production bundle won't have it included.

Surprised there weren't any answers that involved storing environment variables in a .env file with Expo.
I prefer storing my environment variables in a .env file because I don't want to commit certain variables to version control and hardwire them into my application code.
Create your .env file and add your environment variables
Install dotenv
npm install dotenv
In your app.config.js file, load the environment variables from the .env file via dotenv:
require("dotenv").config();
export default {
// config...
}
Expose the environment variables to the Expo runtime in the config in app.config.js:
require("dotenv").config();
export default {
// rest of config...
extra: {
ENV_VAR: process.env.ENV_VAR
}
}
Now you can access your environment variables through the following:
import Constants from "expo-constants";
const ENV_VAR = Constants.expoConfig.extra.ENV_VAR
OPTIONAL: TypeScript
To make using environment variables in our code a little nicer, lets create a typed helper utility to access the environment variables:
import Constants from "expo-constants";
export interface Env {
ENV_VAR: string;
}
/**
* Environment variables exposed through `app.config.js`
* An environment variable not there? Make sure it is explicitly defined in `app.config.js`
*/
export const env = Constants.expoConfig?.extra as Env;
Then you can simply access your environment variables from the env object:
const ENV_VAR = env.ENV_VAR
OPTIONAL: Throw an error if an environment variable is not set
This can be handy to prevent your app from running if an environment variable required for your app to properly function is not set.
In your app.config.js:
// Validate all necessary environment variables are set
const checkForEnvVariable = (envVar) => {
if (!process.env[envVar]) {
throw Error(`${envVar} not set! Check env.md for more information`);
}
};
[
"ENV_VAR",
// ...
].forEach((envVar) => checkForEnvVariable(envVar));

Related

Sveltekit,Supabase and Vercel (problem with Supabase when deploying to Vercel)

I'm trying to set up Sveltekit, Supabase and Vercel.
It works correctly on a local environment (SvelteKit and Supabase), but when I deploy it to Vercel there is a problem with Supabase - "Error: supabaseUrl is required" (I post a screenshot below).
If I don't use Supabase, there are no problems with deploying to Vercel.
Please someone if you have encountered a similar one or have a suggestion to share.
I finally got this to work after doing a couple of things I pieced together from a few sources.
First, I added the the environment variables in Vercel just as the were in the .env file. For example, VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY along with their values.
Next, I added some code in the svelte.config.js file. The result of the file looks like this:
import adapter from '#sveltejs/adapter-auto'
/** #type {import('#sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter(),
vite: {
define: {
'process.env': process.env,
},
},
// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte',
},
}
export default config
I redeployed the project at Vercel, and it worked.
You should add your Supabase URL and Supabase ANON KEY to vercel and stick to the format given below VITE_SUPABASE_URL,VITE_SUPABASE_ANON_KEY if you have initialized according to the supabase guide.
More than adding the configuration to your svelte.config.js file, you should emphasize on adding environment variables to your Vercel environment if you have added this file
// utils/supabase.js
import { createClient } from '#supabase/supabase-js'
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY
export const supabase = createClient(supabaseUrl, supabaseAnonKey)

C-R-A v4: How is the __WB_MANIFEST set? And is it customizable?

I am seeking to better understand PWAs with React, and one topic I am getting stuck on is the __WB_MANIFEST.
I've found that this is required by the Workbox CLI, and is used for precaching files, which is great. It defaults to containing the list of static files built by React, and that makes sense to me.
But is this list customizable? What situations might I want to edit the C-R-A default value? And can I edit it in v4?
Relevant C-R-A doc: https://create-react-app.dev/docs/making-a-progressive-web-app/#customization
Yes, it's customizable, but not out-of-box in cra (proposal, unmerged PR). To config workbox without ejecting, you might want
react-app-rewired
// config-overrides.js
module.exports = function override(config, env) {
if (env === 'development') return config;
const workboxConfig = config.plugins.slice(-1)[0].config;
workboxConfig.exclude = [/.*\.txt/];
// other config...
return config;
};
Or customize-cra:
// config-overrides.js
module.exports = override(
adjustWorkbox(wb =>
Object.assign(wb, {
exclude: (wb.exclude || []).concat("index.html")
// other config...
})
);
);
What situations might I want to edit the C-R-A default value?
Just as examples above, mostly, when you want to exclude something in precache manifest.

Nuxt.js env Property, understanding and how to use it?

following https://nuxtjs.org/api/configuration-env
I have been trying to set up my apiUrl in nuxt.config.js once for the whole project, like:
export default {
env: {
apiUrl: process.env.MY_REMOTE_CMS_API_URL || 'http://localhost:1337'
}
}
adding this in nuxt.config.js, I'd expect (and would like) to have apiUrl accessible everywhere in the project.
In particular, it is needed for the 3 following cases:
with axios, to generate static pages from dynamic urls (in nuxt.config.js)
generate: {
routes: function () {
return axios.get(apiUrl + '/posts')
.then((res) => {
return res.data.filter(page => {
return page.publish === true;
}).map(page => {
return {
route: '/news/' + page.slug
}
})
})
}
},
with apollo, to get data via graphql (in nuxt.config.js)
apollo: {
clientConfigs: {
default: {
httpEndpoint: apiUrl + '/graphql'
}
}
},
in every layout, page and components, as the base url of media:
<img :src="apiUrl + item.image.url" />
As you might see, only thing I need is to 'print' the actual base url of the cms.
I have also tried to access it with process.env.apiUrl, with no success.
The only way I was able to make it has been to create an extra plugin/apiUrl.js file, which injects the api url, and seems wrong to me as I am now setting the apiUrl twice in my project.
I asked this question in the past, but in a way less clear way. I was suggested to use dotenv, but from the docs it looks like adding an additional layer of complication that might not be necessary for a simpler setup.
Thanks.
I think dotenv module really is what you need.
This is my setup:
Project root has a .env file that contains
BASE_URL=https://www.myapi.com
require('dotenv').config() at top of nuxt.config.js
#nuxtjs/dotenv installed and added to buildModules of nuxt.config.js
env: { BASE_URL: process.env.BASE_URL} added to nuxt.config.js
axios: { baseURL: process.env.BASE_URL } added to nuxt.config.js (optional)
You should have access to your .env throughout the project. (process.env.BASE_URL)
I haven't used apollo, but you should be able to set the apollo endpoint with process.env.BASE_URL + '/graphql'
As of Nuxt 2.13, #nuxtjs/dotenv is not required anymore. Read here
The concept that I was missing is that you set up the same named variable in your server / pipeline, so that you have your (always local / never pushed) .env file and a same name variable remotely, not added to your repo (where the value can be the same or different)

Cypress How to store global constants in a file that can be used across all spec files?

I'm looking for a way to store global constants in a file that could be used across all my spec files. Can anyone help?
Use the cypress.json file that is in your project root like this:
{
"env": {
"your_var": "your_value"
}
}
https://docs.cypress.io/guides/references/configuration.html
Once you set some env variables, you can reference them from your specs like this: Cypress.env('your_var');
The following link might help with an easy way to set and get from env variables.
https://docs.cypress.io/api/cypress-api/env.html#Syntax
describe('My First Test', function() {
it('it set value to global variable', function() {
Cypress.env('some_variable', "hello")
})
it('it get value to global variable', function() {
expect(Cypress.env('some_variable')).to.equal('hello')
})
})
I like to create a constants file such as constants.js as an exportable constants object:
export const NAME = {
FIRST: 'John',
LAST: 'Smith',
};
and in my spec files import them: test.spec.js
import { NAME } from '../../support/constants';
describe('Landing page', () => {
beforeEach(() => cy.login());
cy.get(NAME.FIRST).assertion('verify the name');
});
Global variables - sounds like fixtures.
See writefile - JSON - Write response data to a fixture file
cy.request('https://jsonplaceholder.typicode.com/users').then((response) => {
cy.writeFile('cypress/fixtures/users.json', response.body)
})
// our fixture file is now generated and can be used
cy.fixture('users').then((users) => {
expect(users[0].name).to.exist
})
Care to share why you want to do so?
Sounds like it may be interesting.
since cypress is js, it is possible to define const in on js file (my-const.js) as
export const ARE_YOU_SURE='Are you sure';
and use them in another file as
import * as constants from "../[proper path]/my-conste.js";
...
var s = constants.ARE_YOU_SURE + ' about this?'
You can use environment variables stored in either cypress.json or cypress.env.json.
cypress.json
{
"env": {
"myVar": "value..."
}
}
cypress.env.json
{
"myVar": "value..."
}
You can use your environment variable using Cypress.env('myVar').
const myVar = Cypress.env('myVar')
You can review your environment variables in the Settings tab of the cypress runner.

React native require relative path file

I am trying React Native iOS and I was trying to setup project environments. I create a file called config.js so later I can just
import config from 'env'
to load the variables based on different environments. The following is the config.js file
let configFile = 'dev.js'
if (NODE_ENV === 'production') {
configFile = 'prod.js'
}
export default require('./env/' + configFile)
Somehow this won't work. the error message is:
Requiring unknown module "./env/dev.js". If you are sure the module is there, try restarting the packager or running "npm install"
When I changed my code to the following it would not give me errors. But it is not what I wanted to do.
export default require('./env/dev.js')
So does anyone know why is that?
Firstly, require calls are not dynamic. They are statically analyzed and bundled. So you would want something like this
let prodConfig = require('./env/prod.js');
let devConfig = require('./env/dev.js');
let config;
if (process.env.NODE_ENV === 'production') {
config = prodConfig;
}else {
config = devConfig;
}
export default config;