Using Environmental Variables In Firebase Hosting
Tags: firebase, hosting, functions
Published: 2021-01-19 19:26:54

---
title: Using Environmental Variables In Firebase Hosting
published: true
description:
tags: Firebase, hosting, functions
cover_image: https://images.unsplash.com/photo-1599622811903-d4d75fa9dd0f?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80
---
Photo Credit: [Mariko Margetson](https://unsplash.com/@mightyhummingbird)

This article is not really a tutorial or even the answer to a question. In a way it is a question itself. 

Here I explain the problem I was having and how I solved it up to this point, but I'm almost 100% certain that I'm missing some details and understanding.

My hope is that the article might start some arguments, dialogues and conversations and maybe I'll get more context and information about better ways of solving the problem.

### Assumption:
I'm assuming you are generally familiar with Firebase, Firebase Emulators, and Firebase Functions. If not, let me know and I'll see if I can help. I need to write an article about that whole journey anyway.

### Context:

An app I'm working on had some API keys hard-coded in it. I wanted to hide this information, but the application needs some of these keys on start up.

### Problem:

This Flutter app is deployed with Firebase Hosting. The Hosting environment does not have environmental variables. So how do I set and get the environmental variables the app needs without hard-coding and committing keys and secrets?

I followed (as far as I understood it) the advice given by [Frank van Puffelen](https://stackoverflow.com/users/209103/frank-van-puffelen) on [Stack Overflow](https://stackoverflow.com/questions/57119794/flutter-firebase-config-variables).

I know that there are subtlties in his advice that I'm not getting, especially around his comment "If you want them to remain secret, it's not just about not having them in the code, it's about not using them from the client at all." I still don't understand that statement.

### Solution:

In my `functions` directory I created a `.runtimeconfig.json` file (and added it to my `.gitignore`) with the various keys I needed. This is the local development way of setting environmental variables for Firebase Functions.

Then I wrote an `HTTPS` cloud function called `getEnv`. This function returns the Functions `functions.config()` object.

Now I am able to get the keys and secrets I need to start the app by fetching the config object from the `getEnv` function as the app is starting up.

### Final code:

In `functions/.runtimeconfig.json`:

```
{
  "algolia": {
    "appid": "ID",
    "apikey": "KEY"
  },
  "webmerge": {
    "key": "KEY",
    "secret": "SECRET",
    "stashkey": "STASH_KEY"
  },
}
```

In `functions/index.ts`:

```
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';

. . .

const cors = require('cors')({origin: true});
const envObj = functions.config();

. . .

admin.initializeApp();

export const getEnv = functions.https.onRequest((req, resp) => {
  cors(req, resp, () => resp.status(200).send(JSON.stringify(envObj)));
});

. . .
```

NOTE: I used the `cors` package to get around CORS errors when working locally. I would get these errors when `localhost:5000` (Emulator hosting) called `localhost:5001` (Emulator functions).

In `web_flutter/main.dart`:

```
Future<Map<String, dynamic>> fetchEnv(String functionsURL) async {
  var response = await http.get('${functionsURL}/getEnv');
  return json.decode(response.body);
}

Future<void> main() async {
  try {
    var functionsURL = 'FUNCTIONS_URL';
    var app = fb.initializeApp(firebase app details);
    if (window.location.hostname == 'localhost') {
      app.firestore().settings(Settings(
            host: 'localhost:8080',
            ssl: false,
          ));
      functionsURL = 'http://localhost:5001';
    }

    var env = await fetchEnv(functionsURL);

    var searchClient = Algolia.init(
        applicationId: env['algolia']['appid'],
        apiKey: env['algolia']['apikey']);

    runApp(MyApp(
        repository: Repository(app.firestore(), searchClient),
        authentication: Authentication(app.auth())));
  } on fb.FirebaseJsNotLoadedException catch (e) {
    print(e);
  }
}
```

Once I confirmed that this was working locally, I was able to use `firebase functions:config:set` to set this data in the live Functions environment and deploy my updated hosting and functions with `firebase deploy`.

### Your Thoughts?

I'd love to hear your thoughts and experience with environmental variables in general and specific to Firebase. Is there a better way to do this?

Thanks for reading!